From 7fbb93c99acf1f8caf2164cabbf85e2b054a7d6d Mon Sep 17 00:00:00 2001 From: shaharmor Date: Sun, 2 Jul 2017 16:14:23 +0300 Subject: [PATCH 001/388] Detect "Chrome Mobile" & "Firefox Mobile" --- src/ua-parser.js | 16 ++++++++++++---- test/browser-test.json | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 24292f2ab..b3a8c6d26 100644 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -293,7 +293,7 @@ ], [VERSION, [NAME, 'Chrome Headless']], [ /\swv\).+(chrome)\/([\w\.]+)/i // Chrome WebView - ], [[NAME, /(.+)/, '$1 WebView'], VERSION], [ + ], [[NAME, 'Chrome WebView'], VERSION], [ /((?:oculus|samsung)browser)\/([\w\.]+)/i ], [[NAME, /(.+(?:g|us))(.+)/, '$1 $2'], VERSION], [ // Oculus / Samsung Browser @@ -301,8 +301,13 @@ /android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)*/i // Android Browser ], [VERSION, [NAME, 'Android Browser']], [ - /(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i - // Chrome/OmniWeb/Arora/Tizen/Nokia + /(omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i // OmniWeb/Arora/Tizen/Nokia + ], [NAME, VERSION], [ + + /(chrome)\/([\w\.]+) Mobile/i // Chrome Mobile + ], [[NAME, 'Chrome Mobile'], VERSION], [ + + /(chrome)\/v?([\w\.]+)/i // Chrome ], [NAME, VERSION], [ /(dolfin)\/([\w\.]+)/i // Dolphin @@ -314,8 +319,11 @@ /(coast)\/([\w\.]+)/i // Opera Coast ], [[NAME, 'Opera Coast'], VERSION], [ + /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile + ], [[NAME, 'Firefox Mobile'], VERSION], [ + /fxios\/([\w\.-]+)/i // Firefox for iOS - ], [VERSION, [NAME, 'Firefox']], [ + ], [VERSION, [NAME, 'Firefox Mobile']], [ /version\/([\w\.]+).+?mobile\/\w+\s(safari)/i // Mobile Safari ], [VERSION, [NAME, 'Mobile Safari']], [ diff --git a/test/browser-test.json b/test/browser-test.json index d99e3deb1..e39464bfb 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -934,9 +934,39 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) FxiOS/1.1 Mobile/13B143 Safari/601.1.46", "expect" : { - "name" : "Firefox", + "name" : "Firefox Mobile", "version" : "1.1", "major" : "1" } + }, + { + "desc" : "Chrome Mobile", + "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36", + "expect" : + { + "name" : "Chrome Mobile", + "version" : "58.0.3029.83", + "major" : "58" + } + }, + { + "desc" : "Firefox Mobile", + "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) FxiOS/7.5b3349 Mobile/14F89 Safari/603.2.4", + "expect" : + { + "name" : "Firefox Mobile", + "version" : "7.5b3349", + "major" : "7" + } + }, + { + "desc" : "Firefox Mobile", + "ua" : "Mozilla/5.0 (Android 5.0; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0", + "expect" : + { + "name" : "Firefox Mobile", + "version" : "41.0", + "major" : "41" + } } -] +] \ No newline at end of file From 825edbbf91276e5e0843be15b592da090fbc43d4 Mon Sep 17 00:00:00 2001 From: liujunlve Date: Fri, 30 Jul 2021 15:32:56 +0800 Subject: [PATCH 002/388] Fix #509 add support for Internet Explorer 8 --- src/ua-parser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ua-parser.js b/src/ua-parser.js index 2a6236707..3581c9a33 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -114,6 +114,7 @@ // try matching uastring with regexes while (j < regex.length && !matches) { + if (!regex[j]) { break; } matches = regex[j++].exec(ua); if (!!matches) { From 62cc99d7cf56254bf0b131d00136020ba4b4d3f9 Mon Sep 17 00:00:00 2001 From: Paris Morgan Date: Tue, 21 Sep 2021 12:23:49 -0700 Subject: [PATCH 003/388] Samsung Galaxy S20 5G --- src/ua-parser.js | 2 +- test/device-test.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 7d8beb671..2f6711270 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -371,7 +371,7 @@ // Samsung /\b(sch-i[89]0\d|shw-m380s|sm-[pt]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [ - /\b((?:s[cgp]h|gt|sm)-\w+|galaxy nexus)/i, + /\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, /samsung[- ]([-\w]+)/i, /sec-(sgh\w+)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ diff --git a/test/device-test.json b/test/device-test.json index ae9392fe8..4311a78f8 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1404,6 +1404,15 @@ "type": "mobile" } }, + { + "desc": "Samsung Galaxy S20 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; SCG01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SCG01", + "type": "mobile" + } + }, { "desc": "Samsung Galaxy Note 10+", "ua": "Mozilla/5.0 (Linux; Android 9; SM-N976V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36", From 779b5dc1e578cff44c96c60678f8449bd9018fd7 Mon Sep 17 00:00:00 2001 From: KnifeLemon Date: Tue, 12 Apr 2022 12:57:28 +0900 Subject: [PATCH 004/388] add KakaoTalk App, KakaoStory App regex --- src/ua-parser.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 55c3c8d20..e34036772 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -57,7 +57,8 @@ SONY = 'Sony', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', - FACEBOOK = 'Facebook'; + FACEBOOK = 'Facebook', + KAKAO = 'Kakao'; /////////// // Helper @@ -287,6 +288,11 @@ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA']], [ + / wv\).+(chrome)\/([\w\.]+).+(kakaotalk)/i // KakaoTalk App + ], [[NAME, KAKAO+'Talk'], VERSION], [ + / wv\).+(chrome)\/([\w\.]+).+(kakaostory)/i // KakaoStory App + ], [[NAME, KAKAO+'Story'], VERSION], [ + /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ From 0b423078c8966442fdb617b72a2e0195d5aefb40 Mon Sep 17 00:00:00 2001 From: KnifeLemon Date: Tue, 12 Apr 2022 14:17:48 +0900 Subject: [PATCH 005/388] Add Kakao App, Naver App --- src/ua-parser.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index e34036772..3127a9b28 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -58,7 +58,8 @@ XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', FACEBOOK = 'Facebook', - KAKAO = 'Kakao'; + KAKAO = 'Kakao', + NAVER = 'Naver'; /////////// // Helper @@ -223,6 +224,9 @@ /\bopr\/([\w\.]+)/i // Opera Webkit ], [VERSION, [NAME, OPERA]], [ + /whale\/([-\w\.]+).+NAVER\((.*)\)/i // Naver InApp + ], [VERSION,[NAME, /(.+);\s+(.+);\s+(.+);\s+(.+)/i, NAVER+' $1 $2']], [ + // Mixed /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer @@ -288,10 +292,8 @@ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA']], [ - / wv\).+(chrome)\/([\w\.]+).+(kakaotalk)/i // KakaoTalk App - ], [[NAME, KAKAO+'Talk'], VERSION], [ - / wv\).+(chrome)\/([\w\.]+).+(kakaostory)/i // KakaoStory App - ], [[NAME, KAKAO+'Story'], VERSION], [ + / wv\).+chrome\/([\w\.]+).+kakao(.+)\//i // Kakao App + ], [VERSION, [NAME, /(.+)/i, KAKAO+'$1']], [ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ From 534ba7dd7c7648ac27447b0de7bfad33e733bf4e Mon Sep 17 00:00:00 2001 From: KnifeLemon Date: Tue, 12 Apr 2022 16:21:31 +0900 Subject: [PATCH 006/388] Add iOS Naver, Kakao regex --- src/ua-parser.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 3127a9b28..cdf88eac2 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -224,8 +224,9 @@ /\bopr\/([\w\.]+)/i // Opera Webkit ], [VERSION, [NAME, OPERA]], [ - /whale\/([-\w\.]+).+NAVER\((.*)\)/i // Naver InApp - ], [VERSION,[NAME, /(.+);\s+(.+);\s+(.+);\s+(.+)/i, NAVER+' $1 $2']], [ + /Safari\/([-\w\.]+).+NAVER\((.*)\)/i, // Naver InApp for iOS + /whale\/([-\w\.]+).+NAVER\((.*)\)/i // Naver InApp Android + ], [VERSION,[NAME, /(.+); (.+); [0-9]+;.+/i, NAVER+' $1 $2']], [ // Mixed /(kindle)\/([\w\.]+)/i, // Kindle @@ -292,7 +293,8 @@ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA']], [ - / wv\).+chrome\/([\w\.]+).+kakao(.+)\//i // Kakao App + /AppleWebKit\/([\w\.]+).+kakao(.*)\s+/i, // Kakao App for iOS + / wv\).+chrome\/([\w\.]+).+kakao(.+)\//i // Kakao App for Android ], [VERSION, [NAME, /(.+)/i, KAKAO+'$1']], [ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless From 6fde0d92d60752821f214394f337259589b3187e Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Tue, 8 Nov 2022 15:08:50 +0100 Subject: [PATCH 007/388] Support Panasonic Viera Smart TVs --- src/ua-parser.js | 68 ++++++++++++++++++++++++------------------ test/browser-test.json | 10 +++++++ test/device-test.json | 9 ++++++ test/os-test.json | 9 ++++++ 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index b47ad2364..5faa66c67 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -53,9 +53,11 @@ MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', OPERA = 'Opera', + PANASONIC = 'Panasonic', SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', + VIERA = 'Viera', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', FACEBOOK = 'Facebook'; @@ -295,6 +297,8 @@ / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView ], [[NAME, CHROME+' WebView'], VERSION], [ + /Panasonic;(VIERA)/i // Panasonic Viera + ], [[NAME, VIERA]], [ /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser ], [VERSION, [NAME, 'Android '+BROWSER]], [ @@ -367,6 +371,35 @@ device : [[ + /////////////////// + // SMARTTVS + /////////////////// + + /smart-tv.+(samsung)/i // Samsung + ], [VENDOR, [TYPE, SMARTTV]], [ + /hbbtv.+maple;(\d+)/i + ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV + ], [[VENDOR, LG], [TYPE, SMARTTV]], [ + /(apple) ?tv/i // Apple TV + ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ + /crkey/i // Google Chromecast + ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /droid.+aft(\w)( bui|\))/i // Fire TV + ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ + /\(dtv[\);].+(aquos)/i, + /(aquos-tv[\w ]+)\)/i // Sharp + ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ + /(bravia[\w ]+)( bui|\))/i // Sony + ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ + /(mitv-\w{5}) bui/i // Xiaomi + ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ + /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku + /hbbtv\/\d+\.\d+\.\d+ +\([\w ]*; *(\w[^;]*);([^;]*)/i // HbbTV devices + ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors + ], [[TYPE, SMARTTV]], [ + ////////////////////////// // MOBILES & TABLETS // Ordered by popularity @@ -601,35 +634,6 @@ /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [ - /////////////////// - // SMARTTVS - /////////////////// - - /smart-tv.+(samsung)/i // Samsung - ], [VENDOR, [TYPE, SMARTTV]], [ - /hbbtv.+maple;(\d+)/i - ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ - /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV - ], [[VENDOR, LG], [TYPE, SMARTTV]], [ - /(apple) ?tv/i // Apple TV - ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ - /crkey/i // Google Chromecast - ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ - /droid.+aft(\w)( bui|\))/i // Fire TV - ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ - /\(dtv[\);].+(aquos)/i, - /(aquos-tv[\w ]+)\)/i // Sharp - ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ - /(bravia[\w ]+)( bui|\))/i // Sony - ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ - /(mitv-\w{5}) bui/i // Xiaomi - ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ - /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku - /hbbtv\/\d+\.\d+\.\d+ +\([\w ]*; *(\w[^;]*);([^;]*)/i // HbbTV devices - ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors - ], [[TYPE, SMARTTV]], [ - /////////////////// // WEARABLES /////////////////// @@ -729,10 +733,16 @@ /(cros) [\w]+ ([\w\.]+\w)/i // Chromium OS ], [[NAME, 'Chromium OS'], VERSION],[ + // Smart TVs + /Panasonic;VIERA/i // Panasonic Viera + ], [[NAME, VIERA]], [ + + // Console /(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) + // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm /(mint)[\/\(\) ]?(\w*)/i, // Mint diff --git a/test/browser-test.json b/test/browser-test.json index bf8163a1f..4db45f058 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -1278,6 +1278,16 @@ "major" : "1" } }, + { + "desc" : "Viera", + "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", + "expect" : + { + "name" : "Viera", + "version" : "undefined", + "major" : "undefined" + } + }, { "desc" : "Yandex", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5402 Safari/536.5", diff --git a/test/device-test.json b/test/device-test.json index bad1e50af..235b0e605 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1431,6 +1431,15 @@ "type": "mobile" } }, + { + "desc": "Panasonic TX-32CSW514 SmartTV", + "ua": "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", + "expect": { + "vendor": "Panasonic", + "model": "VIERA 2015", + "type": "smarttv" + } + }, { "desc": "Philips SmartTV", "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", diff --git a/test/os-test.json b/test/os-test.json index 57a4d8a95..b7b78fec5 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -1043,6 +1043,15 @@ "version" : "undefined" } }, + { + "desc" : "Panasonic Viera", + "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", + "expect" : + { + "name" : "Viera", + "version" : "undefined" + } + }, { "desc" : "HP-UX", "ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0", From 5fd47ee00d4aa01838da870b6fcf965812622291 Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Tue, 8 Nov 2022 15:26:12 +0100 Subject: [PATCH 008/388] Support Panasonic 2018+ smart tvs --- src/ua-parser.js | 2 +- test/device-test.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 5faa66c67..f825fafff 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -395,7 +395,7 @@ /(mitv-\w{5}) bui/i // Xiaomi ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku - /hbbtv\/\d+\.\d+\.\d+ +\([\w ]*; *(\w[^;]*);([^;]*)/i // HbbTV devices + /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors ], [[TYPE, SMARTTV]], [ diff --git a/test/device-test.json b/test/device-test.json index 235b0e605..d6525f20a 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1440,6 +1440,15 @@ "type": "smarttv" } }, + { + "desc": "Panasonic TX-40FXW724 SmartTV", + "ua": "HbbTV/1.4.1 (+DRM;Panasonic;SmartTV2018mid;3.024;4301-0003 0002-0000;SmartTV2018;)", + "expect": { + "vendor": "Panasonic", + "model": "SmartTV2018mid", + "type": "smarttv" + } + }, { "desc": "Philips SmartTV", "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", From 98096f14548d12befbb49555447aae953b65d074 Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Tue, 8 Nov 2022 15:28:14 +0100 Subject: [PATCH 009/388] Add test for panasonic 2020 smart tv --- test/device-test.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/device-test.json b/test/device-test.json index d6525f20a..154b20e91 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1449,6 +1449,15 @@ "type": "smarttv" } }, + { + "desc": "Panasonic TX-43HXW904 SmartTV", + "ua": "HbbTV/1.5.1 (+DRM;Panasonic;SmartTV2020mid;3.326;4301-0003 0008-0000;com.panasonic.SmartTV2020mid;)", + "expect": { + "vendor": "Panasonic", + "model": "SmartTV2020mid", + "type": "smarttv" + } + }, { "desc": "Philips SmartTV", "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", From 8ffdf232f488a6f59733241ba177538882c8189f Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Tue, 8 Nov 2022 15:30:54 +0100 Subject: [PATCH 010/388] Add test for panasonic SAT receiver --- test/device-test.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/device-test.json b/test/device-test.json index 154b20e91..69323c332 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1458,6 +1458,15 @@ "type": "smarttv" } }, + { + "desc": "Panasonic DMR-HST130 SAT receiver", + "ua": "HbbTV/1.1.1 (+PVR;Panasonic;DIGA WebKit M8658;3.420;;)", + "expect": { + "vendor": "Panasonic", + "model": "DIGA WebKit M8658", + "type": "smarttv" + } + }, { "desc": "Philips SmartTV", "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", From db5567d61487a9940aeceb25eba68bacc1286df4 Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Tue, 8 Nov 2022 15:56:40 +0100 Subject: [PATCH 011/388] Support Loewe Smart TVs --- src/ua-parser.js | 5 ++++- test/device-test.json | 9 +++++++++ test/os-test.json | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index f825fafff..c9d8655bf 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -52,6 +52,7 @@ LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', + NETRANGE= 'Netrange', OPERA = 'Opera', PANASONIC = 'Panasonic', SAMSUNG = 'Samsung', @@ -734,8 +735,10 @@ ], [[NAME, 'Chromium OS'], VERSION],[ // Smart TVs - /Panasonic;VIERA/i // Panasonic Viera + /Panasonic;VIERA/i // Panasonic Viera ], [[NAME, VIERA]], [ + /NETRANGEMMH/i // Netrange + ], [[NAME, NETRANGE]], [ // Console diff --git a/test/device-test.json b/test/device-test.json index 69323c332..f4e4c5b03 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -970,6 +970,15 @@ "type": "smarttv" } }, + { + "desc": "Loewe Smart TV", + "ua": "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", + "expect": { + "vendor": "LOEWE", + "model": "SL410", + "type": "smarttv" + } + }, { "desc": "Meizu M5 Note", "ua": "Mozilla/5.0 (Linux; Android 6.0; M5 Note Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043024 Safari/537.36 MicroMessenger/6.5.7.1040 NetType/WIFI Language/zh_CN", diff --git a/test/os-test.json b/test/os-test.json index b7b78fec5..006504cbf 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -1052,6 +1052,15 @@ "version" : "undefined" } }, + { + "desc" : "Netrange Smart TV", + "ua" : "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", + "expect" : + { + "name" : "Netrange", + "version" : "undefined" + } + }, { "desc" : "HP-UX", "ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0", From a8a4e71345b22f7ba0fce024426d046a5da3183c Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Tue, 8 Nov 2022 16:17:57 +0100 Subject: [PATCH 012/388] Add tests for JVC smart tvs --- test/device-test.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/device-test.json b/test/device-test.json index f4e4c5b03..dd2111a49 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -790,6 +790,24 @@ "type": "mobile" } }, + { + "desc": "JVC LT-43V55LFA Smart TV", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB211 VSTVB MB200 HbbTV/1.2.1 (; JVC; MB211; 3.19.4.2; _TV_NT72563_2017 SmartTvA/3.0.0", + "expect": { + "vendor": "JVC", + "model": "MB211", + "type": "smarttv" + } + }, + { + "desc": "JVC LT-43V65LUA Smart TV", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB130 VSTVB MB100 HbbTV/1.2.1 (; JVC; MB130; 5.7.20.0; _TV_G10_2017;) SmartTvA/3.0.0", + "expect": { + "vendor": "JVC", + "model": "MB130", + "type": "smarttv" + } + }, { "desc": "Lenovo Tab 2", "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36", From 528e7aea1cd1a300043e00f0a7299edc348e3afc Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Wed, 9 Nov 2022 11:03:13 +0100 Subject: [PATCH 013/388] Support philips smart tvs --- src/ua-parser.js | 3 +++ test/device-test.json | 27 +++++++++++++++++++++++++++ test/os-test.json | 9 +++++++++ 3 files changed, 39 insertions(+) diff --git a/src/ua-parser.js b/src/ua-parser.js index c9d8655bf..d8068a0b2 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -53,6 +53,7 @@ MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', NETRANGE= 'Netrange', + NETTV = 'NetTV', OPERA = 'Opera', PANASONIC = 'Panasonic', SAMSUNG = 'Samsung', @@ -739,6 +740,8 @@ ], [[NAME, VIERA]], [ /NETRANGEMMH/i // Netrange ], [[NAME, NETRANGE]], [ + /nettv\/(\d\.\d.\d)/i // NetTV + ], [VERSION, [NAME, NETTV]], [ // Console diff --git a/test/device-test.json b/test/device-test.json index dd2111a49..136b61e0f 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1503,6 +1503,33 @@ "type": "smarttv" } }, + { + "desc": "Philips 32PFL6606K/02 SmartTV (2011)", + "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PFL6606K/02 SmartTV (2013)", + "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PHS5301/12 SmartTV (2016)", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36 OPR/29.0.1803.0 OMI/4.5.23.37.MOT2.13 HbbTV/1.2.1 (;Philips;32PHS5301/12;;_TV_MT5800;) Firmware/TPM161E_012.002.045.001 en", + "expect": { + "vendor": "Philips", + "model": "32PHS5301/12", + "type": "smarttv" + } + }, { "desc": "Roku", "ua": "Mozilla/5.0 (Roku) AppleWebKit/537.36 (KHTML, like Gecko) Web/1.1 Safari/537.36", diff --git a/test/os-test.json b/test/os-test.json index 006504cbf..e5b13c27f 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -1061,6 +1061,15 @@ "version" : "undefined" } }, + { + "desc" : "NetTV 3.2.1", + "ua" : "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70", + "expect" : + { + "name" : "NetTV", + "version" : "3.2.1" + } + }, { "desc" : "HP-UX", "ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0", From e5942dc505ac219872e0da384dd8e4fbe6a738b6 Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Thu, 10 Nov 2022 09:07:08 +0100 Subject: [PATCH 014/388] Add samsung testcases --- test/device-test.json | 9 +++++++++ test/os-test.json | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/test/device-test.json b/test/device-test.json index 136b61e0f..fbdc9f4bf 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1809,6 +1809,15 @@ "type": "smarttv" } }, + { + "desc": "Samsung SmartTV HBBTV", + "ua": "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2021:UAU7000", + "type": "smarttv" + } + }, { "desc": "Sharp AQUOS-TVX19B", "ua": "Mozilla/5.0 (Linux; Android 9; AQUOS-TVX19B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", diff --git a/test/os-test.json b/test/os-test.json index e5b13c27f..3408ddd9d 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -197,6 +197,15 @@ "version" : "2.3" } }, + { + "desc" : "Tizen 6.0", + "ua" : "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", + "expect" : + { + "name" : "Tizen", + "version" : "6.0" + } + }, { "desc" : "Android", "ua" : "Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; VM670 Build/FRG83G) AppleWebKit/533.1 (KHTML, like Gecko)", From df7551c9b69d067cb2a14c91849f0c8715c0af86 Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Thu, 10 Nov 2022 11:20:31 +0100 Subject: [PATCH 015/388] Add support for TechniSAT TVs and SATs --- src/ua-parser.js | 2 ++ test/device-test.json | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/ua-parser.js b/src/ua-parser.js index d8068a0b2..d3895e064 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -396,6 +396,8 @@ ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ /(mitv-\w{5}) bui/i // Xiaomi ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ + /Hbbtv.*(technisat) (.*);/i // TechniSAT + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ diff --git a/test/device-test.json b/test/device-test.json index fbdc9f4bf..fbcc5441f 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -2045,6 +2045,26 @@ "type": "embedded" } }, + { + "desc" : "TechniSAT Digit ISIO S SAT receiver", + "ua" : "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50", + "expect" : + { + "vendor": "TechniSat", + "model": "Digit ISIO S", + "type": "smarttv" + } + }, + { + "desc" : "TechniSAT MultyVision SmartTV", + "ua" : "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50", + "expect" : + { + "vendor": "TechniSat", + "model": "MultyVision ISIO", + "type": "smarttv" + } + }, { "desc": "Xiaomi 2013023", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; en-US; 2013023 Build/HM2013023) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/10.0.1.512 U3/0.8.0 Mobile Safari/533.1", From cf093222e5a2dbdff1855539144e457c9c47a9d1 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 24 Jan 2023 00:42:40 +0700 Subject: [PATCH 016/388] Fix #557 #612 #629 - Update the changelog --- changelog.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 05fc6b90b..addaff4d3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,49 @@ # UAParser.js Changelog -## Version 0.8.0 \ No newline at end of file +# Version 0.7 / 1.0 + +Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. + +## Version 0.7.30 / 1.0.1 + +- Add new browser : Obigo, UP.Browser, Klar +- Add new device : Oculus, Roku +- Add new OS: Maemo, HP-UX, Android-x86, Deepin, elementary OS, GhostBSD, Linspire, Manjaro, Sabayon +- Improve detection for Sony Xperia 1ii, LG Android TV, and some more devices +- Improve detection for ARM64 CPU +- Improve detection for Windows Mobile, Netscape, Mac on PowerPC +- Categorize PDA as mobile +- Fix Sharp devices misjudged as Huawei +- Fix trailing comma for ES3 compatibility +- Some code refactor + +## Version 0.7.31 / 1.0.2 + +- Fix OPPO Reno A5 incorrect detection +- Fix TypeError Bug +- Use AST to extract regexes and verify them with safe-regex + +## Version 0.7.32 / 1.0.32 + +- Add new browser : DuckDuckGo, Huawei Browser, LinkedIn +- Add new OS : HarmonyOS +- Add some Huawei models +- Add Sharp Aquos TV +- Improve detection Xiaomi Mi CC9 +- Fix Sony Xperia 1 III misidentified as Acer tablet +- Fix Detect Sony BRAVIA as SmartTV +- Fix Detect Xiaomi Mi TV as SmartTV +- Fix Detect Galaxy Tab S8 as tablet +- Fix WeGame mistakenly identified as WeChat +- Fix included commas in Safari / Mobile Safari version +- Increase UA_MAX_LENGTH to 350 + +## Version 0.7.33 / 1.0.33 + +- Add new browser : Cobalt +- Identify Macintosh as an Apple device +- Fix ReDoS vulnerability + +# Version 0.8 + +Version 0.8 was created by accident. This version is now deprecated and no longer maintained, please update to version 0.7 / 1.0. \ No newline at end of file From 5d4ca2b4c2fec8d07218726b0bbc97abfc6c6323 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 24 Jan 2023 23:28:03 +0700 Subject: [PATCH 017/388] Fix #621 - Detect Oculus Quest Pro --- src/ua-parser.js | 2 +- test/device-test.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 9e3c71518..31bc93ae1 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -645,7 +645,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ - /(quest( 2)?)/i // Oculus Quest + /(quest( 2| pro)?)/i // Oculus Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ /////////////////// diff --git a/test/device-test.json b/test/device-test.json index 33bfd1717..bb2952866 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1206,6 +1206,15 @@ "type": "wearable" } }, + { + "desc": "Oculus Quest Pro", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest Pro) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/24.4.0.22.60.426469926 SamsungBrowser/4.0 Chrome/106.0.5249.181 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest Pro", + "type": "wearable" + } + }, { "desc": "OnePlus One", "ua": "Mozilla/5.0 (Linux; Android 4.4.4; A0001 Build/KTU84Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.59 Mobile Safari/537.36", From 1a521b867ff5b23d2a75eb5d62e10e22d78998ee Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 25 Jan 2023 01:05:50 +0700 Subject: [PATCH 018/388] Utilize navigator.userAgentData as a fallback #588 --- src/ua-parser.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ua-parser.js b/src/ua-parser.js index 31bc93ae1..1ea234401 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -775,6 +775,7 @@ } var _ua = ua || ((typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY); + var _uach = (typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgentData) ? window.navigator.userAgentData : undefined; var _rgxmap = extensions ? extend(regexes, extensions) : regexes; this.getBrowser = function () { @@ -797,6 +798,9 @@ _device[MODEL] = undefined; _device[TYPE] = undefined; rgxMapper.call(_device, _ua, _rgxmap.device); + if (!_device[TYPE] && _uach && _uach.mobile) { + _device[TYPE] = MOBILE; + } return _device; }; this.getEngine = function () { @@ -811,6 +815,9 @@ _os[NAME] = undefined; _os[VERSION] = undefined; rgxMapper.call(_os, _ua, _rgxmap.os); + if (!_os[NAME] && _uach && _uach.platform != 'Unknown') { + _os[NAME] = _uach.platform.replace(/chrome/i, 'Chromium').replace(/mac/i, 'Mac '); + } return _os; }; this.getResult = function () { From ba0f25c19dcdc7fb41e49b271611536d9fa7b6cf Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 26 Jan 2023 09:35:54 +0700 Subject: [PATCH 019/388] Improve Kakao/Naver detection + add test --- src/ua-parser.js | 16 ++++----------- test/browser-test.json | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 25afb6398..34cabc461 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -58,9 +58,7 @@ SONY = 'Sony', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', - FACEBOOK = 'Facebook', - KAKAO = 'Kakao', - NAVER = 'Naver'; + FACEBOOK = 'Facebook'; /////////// // Helper @@ -226,10 +224,6 @@ /\bopr\/([\w\.]+)/i // Opera Webkit ], [VERSION, [NAME, OPERA]], [ - /Safari\/([-\w\.]+).+NAVER\((.*)\)/i, // Naver InApp for iOS - /whale\/([-\w\.]+).+NAVER\((.*)\)/i // Naver InApp Android - ], [VERSION,[NAME, /(.+); (.+); [0-9]+;.+/i, NAVER+' $1 $2']], [ - // Mixed /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer @@ -239,7 +233,7 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ @@ -290,6 +284,8 @@ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android ], [[NAME, FACEBOOK], VERSION], [ + /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App + /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram @@ -297,10 +293,6 @@ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA']], [ - /AppleWebKit\/([\w\.]+).+kakao(.*)\s+/i, // Kakao App for iOS - / wv\).+chrome\/([\w\.]+).+kakao(.+)\//i // Kakao App for Android - ], [VERSION, [NAME, /(.+)/i, KAKAO+'$1']], [ - /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ diff --git a/test/browser-test.json b/test/browser-test.json index 2be58c21b..08f8489c0 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -1627,5 +1627,50 @@ "version": "9.0", "major" : "9" } + }, + { + "desc" : "KakaoTalk App Android", + "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36;KAKAOTALK 2409760", + "expect" : { + "name" : "KAKAOTALK", + "version": "2409760", + "major" : "2409760" + } + }, + { + "desc" : "KakaoStory App Android", + "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36 KAKAOSTORY/6.8.3_21046", + "expect" : { + "name" : "KAKAOSTORY", + "version": "6.8.3_21046", + "major" : "6" + } + }, + { + "desc" : "KakaoTalk App iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU; iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 BizWebView KAKAOTALK 9.7.6", + "expect" : { + "name" : "KAKAOTALK", + "version": "9.7.6", + "major" : "9" + } + }, + { + "desc" : "Naver App Android", + "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.232 Whale/1.0.0.0 Crosswalk/26.90.3.21 Mobile Safari/537.36 NAVER(inapp; search; 1010; 11.11.2)", + "expect" : { + "name" : "NAVER", + "version": "11.11.2", + "major" : "11" + } + }, + { + "desc" : "Naver App iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/605.1 NAVER(inapp; search; 720; 10.25.0; 11PRO)", + "expect" : { + "name" : "NAVER", + "version": "10.25.0", + "major" : "10" + } } ] From 0b0c7ff6171249f94cafbac4153ae9b9ea704e1f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 26 Jan 2023 10:26:38 +0700 Subject: [PATCH 020/388] Fix #619 - Move Sharp up to be checked before Huawei --- src/ua-parser.js | 9 ++++----- test/device-test.json | 9 +++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 34cabc461..99f75077a 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -375,7 +375,6 @@ ////////////////////////// // MOBILES & TABLETS - // Ordered by popularity ///////////////////////// // Samsung @@ -396,6 +395,10 @@ /(macintosh);/i ], [MODEL, [VENDOR, APPLE]], [ + // Sharp + /\b(sh-?[altvz]?\d\d[a-ekm]?)/i + ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ + // Huawei /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ @@ -511,10 +514,6 @@ /\bmz-([-\w]{2,})/i ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [ - // Sharp - /\b(sh-?[altvz]?\d\d[a-ekm]?)/i - ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ - // MIXED /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron diff --git a/test/device-test.json b/test/device-test.json index bb2952866..08874210a 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1764,6 +1764,15 @@ "type": "mobile" } }, + { + "desc": "Sharp Aquos L2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-L02) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-L02", + "type": "mobile" + } + }, { "desc": "Sharp Aquos R2", "ua": "Mozilla/5.0 (Linux; Android 8.0; SHV42) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36", From 0cbeb7a82966aac71e7cadb4794669dabd339868 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 26 Jan 2023 14:03:48 +0700 Subject: [PATCH 021/388] Rearrange the recently added smarttv detection --- readme.md | 36 +++++++++--------- src/ua-parser.js | 83 +++++++++++++++++++----------------------- test/browser-test.json | 2 +- test/device-test.json | 9 +++++ test/os-test.json | 6 +-- 5 files changed, 68 insertions(+), 68 deletions(-) diff --git a/readme.md b/readme.md index 71bb1879d..436f038ea 100644 --- a/readme.md +++ b/readme.md @@ -80,19 +80,19 @@ The methods are self explanatory, here's a small overview on all the available m # Possible 'browser.name': 2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG, BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera, -Chrome Headless, Chrome WebView, Chrome, Chromium, Comodo Dragon, Dillo, +Chrome Headless, Chrome WebView, Chrome, Chromium, Cobalt, Comodo Dragon, Dillo, Dolphin, Doris, DuckDuckGo, Edge, Electron, Epiphany, Facebook, Falkon, Fennec, Firebird, Firefox [Focus/Reality], Flock, Flow, GSA, GoBrowser, Huawei Browser, ICE Browser, IE, IEMobile, IceApe, IceCat, IceDragon, Iceweasel, Instagram, -Iridium, Iron, Jasmine, K-Meleon, Kindle, Klar, Konqueror, LBBROWSER, Line, -LinkedIn, Links, Lunascape, Lynx, MIUI Browser, Maemo Browser, Maemo, Maxthon, -MetaSr Midori, Minimo, Mobile Safari, Mosaic, Mozilla, NetFront, NetSurf, Netfront, -Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb, Opera Coast, -Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris, Puffin, QQ, -QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, Safari, Sailfish Browser, -Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim, SlimBrowser, Swiftfox, -Tesla, Tizen Browser, UCBrowser, UP.Browser, Vivaldi, Waterfox, WeChat, Weibo, -Yandex, baidu, iCab, w3m, Whale Browser... +Iridium, Iron, Jasmine, Kakao[Story/Talk], K-Meleon, Kindle, Klar, Konqueror, +LBBROWSER, Line, LinkedIn, Links, Lunascape, Lynx, MIUI Browser, Maemo Browser, +Maemo, Maxthon, MetaSr Midori, Minimo, Mobile Safari, Mosaic, Mozilla, NetFront, +NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb, +Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris, +Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, Safari, +Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim, +SlimBrowser, Swiftfox, Tesla, Tizen Browser, UCBrowser, UP.Browser, Viera, +Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser... # 'browser.version' determined dynamically ``` @@ -113,10 +113,10 @@ console, mobile, tablet, smarttv, wearable, embedded # Possible 'device.vendor': Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell, -Essential, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Lenovo, LG, -Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus, OPPO, Ouya, -Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp, Siemens, -Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ... +Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, +Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus, +OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp, +Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ... # 'device.model' determined dynamically ``` @@ -141,10 +141,10 @@ AIX, Amiga OS, Android[-x86], Arch, Bada, BeOS, BlackBerry, CentOS, Chromium OS, Contiki, Fedora, Firefox OS, FreeBSD, Debian, Deepin, DragonFly, elementary OS, Fuchsia, Gentoo, GhostBSD, GNU, Haiku, HarmonyOS, HP-UX, Hurd, iOS, Joli, KaiOS, Linpus, Linspire,Linux, Mac OS, Maemo, Mageia, Mandriva, Manjaro, MeeGo, Minix, -Mint, Morph OS, NetBSD, Nintendo, OpenBSD, OpenVMS, OS/2, Palm, PC-BSD, PCLinuxOS, -Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS, RISC OS, Sabayon, -Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen, Ubuntu, Unix, -VectorLinux, WebOS, Windows [Phone/Mobile], Zenwalk, ... +Mint, Morph OS, NetBSD, NetRange, NetTV, Nintendo, OpenBSD, OpenVMS, OS/2, Palm, +PC-BSD, PCLinuxOS, Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS, +RISC OS, Sabayon, Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen, +Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ... # 'os.version' determined dynamically ``` diff --git a/src/ua-parser.js b/src/ua-parser.js index 45ebee3fc..ec23bc94d 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -52,10 +52,7 @@ LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', - NETRANGE= 'Netrange', - NETTV = 'NetTV', OPERA = 'Opera', - PANASONIC = 'Panasonic', SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', @@ -302,8 +299,6 @@ / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView ], [[NAME, CHROME+' WebView'], VERSION], [ - /Panasonic;(VIERA)/i // Panasonic Viera - ], [[NAME, VIERA]], [ /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser ], [VERSION, [NAME, 'Android '+BROWSER]], [ @@ -338,7 +333,8 @@ // Other /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i // Links + /(links) \(([\w\.]+)/i, // Links + /panasonic;(viera)/i // Panasonic Viera ], [NAME, VERSION], [ /(cobalt)\/([\w\.]+)/i // Cobalt @@ -379,37 +375,6 @@ device : [[ - /////////////////// - // SMARTTVS - /////////////////// - - /smart-tv.+(samsung)/i // Samsung - ], [VENDOR, [TYPE, SMARTTV]], [ - /hbbtv.+maple;(\d+)/i - ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ - /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV - ], [[VENDOR, LG], [TYPE, SMARTTV]], [ - /(apple) ?tv/i // Apple TV - ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ - /crkey/i // Google Chromecast - ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ - /droid.+aft(\w)( bui|\))/i // Fire TV - ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ - /\(dtv[\);].+(aquos)/i, - /(aquos-tv[\w ]+)\)/i // Sharp - ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ - /(bravia[\w ]+)( bui|\))/i // Sony - ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ - /(mitv-\w{5}) bui/i // Xiaomi - ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ - /Hbbtv.*(technisat) (.*);/i // TechniSAT - ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ - /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku - /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices - ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors - ], [[TYPE, SMARTTV]], [ - ////////////////////////// // MOBILES & TABLETS ///////////////////////// @@ -539,7 +504,7 @@ // ZTE /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i, - /(alcatel|geeksphone|nexian|panasonic|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony + /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ // Acer @@ -631,6 +596,37 @@ /droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [ + /////////////////// + // SMARTTVS + /////////////////// + + /smart-tv.+(samsung)/i // Samsung + ], [VENDOR, [TYPE, SMARTTV]], [ + /hbbtv.+maple;(\d+)/i + ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV + ], [[VENDOR, LG], [TYPE, SMARTTV]], [ + /(apple) ?tv/i // Apple TV + ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ + /crkey/i // Google Chromecast + ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /droid.+aft(\w)( bui|\))/i // Fire TV + ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ + /\(dtv[\);].+(aquos)/i, + /(aquos-tv[\w ]+)\)/i // Sharp + ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ + /(bravia[\w ]+)( bui|\))/i // Sony + ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ + /(mitv-\w{5}) bui/i // Xiaomi + ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ + /Hbbtv.*(technisat) (.*);/i // TechniSAT + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ + /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku + /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices + ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors + ], [[TYPE, SMARTTV]], [ + /////////////////// // CONSOLES /////////////////// @@ -745,19 +741,14 @@ ], [[NAME, 'Chromium OS'], VERSION],[ // Smart TVs - /Panasonic;VIERA/i // Panasonic Viera - ], [[NAME, VIERA]], [ - /NETRANGEMMH/i // Netrange - ], [[NAME, NETRANGE]], [ - /nettv\/(\d\.\d.\d)/i // NetTV - ], [VERSION, [NAME, NETTV]], [ - + /panasonic;(viera)/i, // Panasonic Viera + /(netrange)mmh/i, // Netrange + /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV // Console /(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) - // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm /(mint)[\/\(\) ]?(\w*)/i, // Mint diff --git a/test/browser-test.json b/test/browser-test.json index 440e4d4a7..ef0cb34f2 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -1283,7 +1283,7 @@ "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", "expect" : { - "name" : "Viera", + "name" : "VIERA", "version" : "undefined", "major" : "undefined" } diff --git a/test/device-test.json b/test/device-test.json index 8dcb4d0cc..4f44e84b4 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1476,6 +1476,15 @@ "type": "mobile" } }, + { + "desc": "Panasonic T31", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Panasonic T31 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Mobile Safari/537.36 ", + "expect": { + "vendor": "Panasonic", + "model": "T31", + "type": "mobile" + } + }, { "desc": "Panasonic TX-32CSW514 SmartTV", "ua": "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", diff --git a/test/os-test.json b/test/os-test.json index 3408ddd9d..e18d80861 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -1057,7 +1057,7 @@ "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", "expect" : { - "name" : "Viera", + "name" : "VIERA", "version" : "undefined" } }, @@ -1066,7 +1066,7 @@ "ua" : "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", "expect" : { - "name" : "Netrange", + "name" : "NETRANGE", "version" : "undefined" } }, @@ -1075,7 +1075,7 @@ "ua" : "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70", "expect" : { - "name" : "NetTV", + "name" : "NETTV", "version" : "3.2.1" } }, From f815ca6e9cabce4099103b21423d7a9c4531c57a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 26 Jan 2023 14:58:53 +0700 Subject: [PATCH 022/388] Fix #620 - Add new Device: Kobo --- readme.md | 2 +- src/ua-parser.js | 1 + test/device-test.json | 20 +++++++++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 436f038ea..a45e17e89 100644 --- a/readme.md +++ b/readme.md @@ -113,7 +113,7 @@ console, mobile, tablet, smarttv, wearable, embedded # Possible 'device.vendor': Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell, -Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, +Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Kobo, Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus, OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp, Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ... diff --git a/src/ua-parser.js b/src/ua-parser.js index ec23bc94d..4a22f671c 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -527,6 +527,7 @@ /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + /(kobo)\s(ereader|touch)/i, // Kobo /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad /(kindle)\/([\w\.]+)/i, // Kindle diff --git a/test/device-test.json b/test/device-test.json index 4f44e84b4..f042a17db 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -817,6 +817,24 @@ "type": "smarttv" } }, + { + "desc": "Kobo eReader", + "ua": "Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Kobo eReader Safari/538.1", + "expect": { + "vendor": "Kobo", + "model": "eReader", + "type": "tablet" + } + }, + { + "desc": "Kobo Touch", + "ua": "Mozilla/5.0 (Linux; U; Android 2.0; en-us;) AppleWebKit/538.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/538.1 (Kobo Touch 0377/4.20.14622)", + "expect": { + "vendor": "Kobo", + "model": "Touch", + "type": "tablet" + } + }, { "desc": "Lenovo Tab 2", "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36", @@ -826,7 +844,7 @@ "type": "tablet" } }, - { + { "desc": "Lenovo Phone", "ua": "Mozilla/5.0 (Linux; Android 6.0; Lenovo PB2-650M Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/311.0.0.44.117;]", "expect": { From f8e5a1fb4fbe55b781a48d7d141fdc41ba93fa38 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 31 Jan 2023 15:09:14 +0700 Subject: [PATCH 023/388] Fix #601 - Detect Chrome OS without version --- src/ua-parser.js | 2 +- test/os-test.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 031faa362..ac7cbd3cb 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -738,7 +738,7 @@ // Google Chromecast /crkey\/([\d\.]+)/i // Google Chromecast ], [VERSION, [NAME, CHROME+'cast']], [ - /(cros) [\w]+ ([\w\.]+\w)/i // Chromium OS + /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS ], [[NAME, 'Chromium OS'], VERSION],[ // Smart TVs diff --git a/test/os-test.json b/test/os-test.json index e18d80861..20670158d 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -665,6 +665,15 @@ "version" : "1.9.2.22-0.1mdv2010.2" } }, + { + "desc" : "Chrome OS", + "ua" : "Mozilla/5.0 (X11; CrOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chromium OS", + "version" : "undefined" + } + }, { "desc" : "Chromium OS", "ua" : "Mozilla/5.0 (X11; CrOS x86_64 10575.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", From 6c34c3d4faddc9fee686ef6161bc16f5d0882045 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 14 Feb 2023 23:28:09 +0700 Subject: [PATCH 024/388] Add new utility methods: is() & toString() --- dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- readme.md | 147 +++++++++++++++++++++++++++++------------ src/ua-parser.js | 115 +++++++++++++++++++++++--------- test/test.js | 98 ++++++++++++++++++++++++++- 5 files changed, 290 insertions(+), 78 deletions(-) diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index d5869863f..767bddce0 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v0.7.31 +/* UAParser.js v0.7.32 Copyright © 2012-2021 Faisal Salman MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="0.7.31",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",UA_MAX_LENGTH=350;var AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",BROWSER="Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",OPERA="Opera",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",FACEBOOK="Facebook";var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){mergedRegexes[i]=extensions[i].concat(regexes[i])}else{mergedRegexes[i]=regexes[i]}}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;jUA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this};this.setUA(_ua);return this};UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="0.7.32",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",UA_MAX_LENGTH=350;var AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",BROWSER="Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",OPERA="Opera",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",FACEBOOK="Facebook";var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){mergedRegexes[i]=extensions[i].concat(regexes[i])}else{mergedRegexes[i]=regexes[i]}}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;jUA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this};this.setUA(_ua);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 18c0cdcd6..5c33553bd 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v0.7.31 +/* UAParser.js v0.7.32 Copyright © 2012-2021 Faisal Salman MIT License */ -!function(r,d){"use strict";function i(i){for(var e={},o=0;oS?L(i,S):i,this},this.setUA(o),this}Q.prototype.is=function(i){for(var e in this.propIs)if(o(this[this.propIs[e]],this.rgxIs)==o(i,this.rgxIs))return!0;return!1},Q.prototype.toString=function(){var i,e="";for(i in this.propToString)typeof this[this.propToString[i]]!==s&&(e+=(e?" ":"")+this[this.propToString[i]]);return e||s},Y.prototype=new Q([l,h],[[l],/\s?browser$/i]),J.prototype=new Q([f],[[f]]),ii.prototype=new Q([m,r],[[p,r,m]]),ei.prototype=new Q([l,h],[[l]]),oi.prototype=new Q([l,h],[[l],/\s?os$/i]),ti.VERSION="0.7.32",ti.BROWSER=i([l,h,w]),ti.CPU=i([f]),ti.DEVICE=i([r,m,p,g,v,k,x,y,_]),ti.ENGINE=ti.OS=i([l,h]),typeof exports!==s?(typeof module!==s&&module.exports&&(exports=module.exports=ti),exports.UAParser=ti):typeof define==u&&define.amd?define(function(){return ti}):typeof a!==s&&(a.UAParser=ti);var ai,ni=typeof a!==s&&(a.jQuery||a.Zepto);ni&&!ni.ua&&(ai=new ti,ni.ua=ai.getResult(),ni.ua.get=function(){return ai.getUA()},ni.ua.set=function(i){ai.setUA(i);var e,o=ai.getResult();for(e in o)ni.ua[e]=o[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/readme.md b/readme.md index a45e17e89..514e949d9 100644 --- a/readme.md +++ b/readme.md @@ -19,10 +19,7 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro * Source : https://github.com/faisalman/ua-parser-js # Documentation -### UAParser([user-agent][,extensions]) -typeof `user-agent` "string". - -typeof `extensions` "array". +### UAParser([user-agent:string][,extensions:object]) In The Browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in nodejs for the function to work. Usually you can find the user agent in: @@ -32,7 +29,7 @@ Usually you can find the user agent in: ## Constructor When you call `UAParser` with the `new` keyword `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string. Like so: -* `new UAParser([uastring][,extensions])` +* `new UAParser([user-agent:string][,extensions:object])` ```js let parser = new UAParser("user-agent"); // you need to pass the user-agent for nodejs console.log(parser); // {} @@ -49,7 +46,7 @@ console.log(parserResults); ``` When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results. -* `UAParser([uastring][,extensions])` +* `UAParser([user-agent:string][,extensions:object])` * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` ## Methods @@ -67,13 +64,12 @@ The methods are self explanatory, here's a small overview on all the available m * `getUA()` - returns the user-agent string. * `setUA(user-agent)` - set a custom user-agent to be parsed. - --- -* `getResult()` - * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` +* `getResult() : UAResult` + * returns `{ ua: '', browser: UABrowser {}, cpu: UACPU {}, device: UADevice {}, engine: UAEngine {}, os: UAOS {} }` -* `getBrowser()` +* `getBrowser() : UABrowser` * returns `{ name: '', version: '' }` ```sh @@ -97,7 +93,7 @@ Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser... # 'browser.version' determined dynamically ``` -* `getDevice()` +* `getDevice() : UADevice` * returns `{ model: '', type: '', vendor: '' }` ```sh @@ -121,7 +117,7 @@ Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE # 'device.model' determined dynamically ``` -* `getEngine()` +* `getEngine() : UAEngine` * returns `{ name: '', version: '' }` ```sh @@ -132,7 +128,7 @@ NetSurf, Presto, Tasman, Trident, w3m, WebKit # 'engine.version' determined dynamically ``` -* `getOS()` +* `getOS() : UAOS` * returns `{ name: '', version: '' }` ```sh @@ -149,7 +145,7 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ... # 'os.version' determined dynamically ``` -* `getCPU()` +* `getCPU() : UACPU` * returns `{ architecture: '' }` ```sh @@ -157,13 +153,92 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ... 68k, amd64, arm[64/hf], avr, ia[32/64], irix[64], mips[64], pa-risc, ppc, sparc[64] ``` -* `getUA()` +* `getUA() : string` * returns UA string of current instance * `setUA(uastring)` * set UA string to be parsed * returns current instance +#### * is() utility `since@1.1` + +```js +let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); + +uap.getBrowser().name; // "IEMobile" +uap.getBrowser().is("IEMobile"); // true +uap.getCPU().is("ARM"); // true + +uap.getOS().name; // "Windows Phone" +uap.getOS().is("Windows Phone"); // true + +uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" } +uap.getResult().device; // { vendor: "Nokia", model: "Lumia 635", type: "mobile" } + +uap.getDevice().is("mobile"); // true +uap.getDevice().is("Lumia 635"); // true +uap.getDevice().is("Nokia"); // true +uap.getDevice().is("iPhone"); // false +uap.getResult().device.is("Nokia"); // true +uap.getResult().device.model; // "Lumia 635" + +uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); +uap.getBrowser().is("IEMobile"); // false +uap.getBrowser().is("Chrome"); // true +uap.getResult().browser.is("Edge"); // false +uap.getResult().os.is("Mac OS"); // true +uap.getResult().os.version; // "10.6.8" +uap.getEngine().is("Blink"); // true +``` + +#### * toString() utility `since@1.1` + +```js +let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); + +uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" } +uap.getDevice().toString(); // "Nokia Lumia 635" + +uap.getResult().os.name; // "Windows Phone" +uap.getResult().os.version; // "8.1" +uap.getResult().os.toString(); // "Windows Phone 8.1" + +uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); +uap.getBrowser().name; // Chrome +uap.getBrowser().version; // 28.0.1500.95 +uap.getBrowser().major; // 28 +uap.getBrowser().toString(); // "Chrome 28.0.1500.95" +``` + +## Extending Regex + +If you want to detect something that's not currently provided by UAParser.js (eg: bots, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own. + +* `UAParser([uastring,] extensions)` + +```js +// Example: +let myOwnListOfBrowsers = [ + [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION] +]; +let myParser = new UAParser({ browser: myOwnListOfBrowsers }); +let myUA = 'Mozilla/5.0 MyBrowser/1.3'; +console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3"} + +// Another example: +let myOwnListOfDevices = [ + [/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]], + [/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]] +]; +let myParser2 = new UAParser({ + browser: myOwnListOfBrowsers, + device: myOwnListOfDevices +}); +let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; +console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"} +``` + + # Usage ## Using HTML @@ -175,8 +250,8 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ... @@ -248,11 +323,11 @@ $ npm install ua-parser-js ```js var http = require('http'); -var parser = require('ua-parser-js'); +var uap = require('ua-parser-js'); http.createServer(function (req, res) { // get user-agent header - var ua = parser(req.headers['user-agent']); + var ua = uap(req.headers['user-agent']); // write the result as response res.end(JSON.stringify(ua, null, ' ')); }) @@ -296,20 +371,6 @@ console.log(parseInt($.ua.browser.version.split('.')[0], 10)); // 4 $('body').addClass('ua-browser-' + $.ua.browser.name + ' ua-devicetype-' + $.ua.device.type); ``` -## Using Extension - -* `UAParser([uastring,] extensions)` - -```js -// Example: -var myOwnListOfBrowsers = [ - [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION] -]; -var myParser = new UAParser({ browser: myOwnListOfBrowsers }); -var myUA = 'Mozilla/5.0 MyBrowser/1.3'; -console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3"} -``` - # Development ## Sponsors diff --git a/src/ua-parser.js b/src/ua-parser.js index 4a22f671c..c38b14d99 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -17,7 +17,7 @@ ///////////// - var LIBVERSION = '0.7.32', + var LIBVERSION = '1.1.34', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -56,7 +56,6 @@ SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', - VIERA = 'Viera', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', FACEBOOK = 'Facebook'; @@ -87,11 +86,14 @@ return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, lowerize = function (str) { - return str.toLowerCase(); + return typeof(str) === STR_TYPE ? str.toLowerCase() : UNDEF_TYPE; }, majorize = function (version) { return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] : undefined; }, + sanitize = function (str, rgx) { + return rgx ? lowerize(str).replace(rgx, EMPTY) : lowerize(str); + }, trim = function (str, len) { if (typeof(str) === STR_TYPE) { str = str.replace(/^\s\s*/, EMPTY); @@ -200,6 +202,10 @@ '8.1' : 'NT 6.3', '10' : ['NT 6.4', 'NT 10.0'], 'RT' : 'ARM' + }, + archEquivalenceMap = { + 'amd64' : ['x86-64', 'x64'], + 'ia32' : ['x86'] }; ////////////// @@ -775,7 +781,70 @@ // Constructor //////////////// - var UAParser = function (ua, extensions) { + function UAItem (propToString, propIs) { + this.propToString = propToString; + this.propIs = propIs[0]; + this.rgxIs = propIs[1]; + } + UAItem.prototype.is = function (strCheck) { + for (var i in this.propIs) { + if (sanitize(this[this.propIs[i]], this.rgxIs) == sanitize(strCheck, this.rgxIs)) { + return true; + } + } + return false; + }; + UAItem.prototype.toString = function () { + var str = ''; + for (var i in this.propToString) { + if (typeof(this[this.propToString[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : '') + this[this.propToString[i]]; + } + } + return str ? str : UNDEF_TYPE; + }; + + function UABrowser () { + this[NAME] = undefined; + this[VERSION] = undefined; + this[MAJOR] = undefined; + } + UABrowser.prototype = new UAItem([NAME, VERSION], [[NAME], /\s?browser$/i]); + + function UACPU () { + this[ARCHITECTURE] = undefined; + } + UACPU.prototype = new UAItem([ARCHITECTURE], [[ARCHITECTURE]]); + + function UADevice () { + this[VENDOR] = undefined; + this[MODEL] = undefined; + this[TYPE] = undefined; + } + UADevice.prototype = new UAItem([VENDOR, MODEL], [[TYPE, MODEL, VENDOR]]); + + function UAEngine () { + this[NAME] = undefined; + this[VERSION] = undefined; + } + UAEngine.prototype = new UAItem([NAME, VERSION], [[NAME]]); + + function UAOS () { + this[NAME] = undefined; + this[VERSION] = undefined; + } + UAOS.prototype = new UAItem([NAME, VERSION], [[NAME], /\s?os$/i]); + + function UAResult (uap) { + this.ua = uap.getUA(); + this.browser = uap.getBrowser(); + this.cpu = uap.getCPU(); + this.device = uap.getDevice(); + this.engine = uap.getEngine(); + this.os = uap.getOS(); + } + + function UAParser (ua, extensions) { if (typeof ua === OBJ_TYPE) { extensions = ua; @@ -790,25 +859,20 @@ var _uach = (typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgentData) ? window.navigator.userAgentData : undefined; var _rgxmap = extensions ? extend(regexes, extensions) : regexes; + // public methods this.getBrowser = function () { - var _browser = {}; - _browser[NAME] = undefined; - _browser[VERSION] = undefined; + var _browser = new UABrowser(); rgxMapper.call(_browser, _ua, _rgxmap.browser); - _browser.major = majorize(_browser.version); + _browser[MAJOR] = majorize(_browser[VERSION]); return _browser; }; this.getCPU = function () { - var _cpu = {}; - _cpu[ARCHITECTURE] = undefined; + var _cpu = new UACPU(); rgxMapper.call(_cpu, _ua, _rgxmap.cpu); return _cpu; }; this.getDevice = function () { - var _device = {}; - _device[VENDOR] = undefined; - _device[MODEL] = undefined; - _device[TYPE] = undefined; + var _device = new UADevice(); rgxMapper.call(_device, _ua, _rgxmap.device); if (!_device[TYPE] && _uach && _uach.mobile) { _device[TYPE] = MOBILE; @@ -816,31 +880,22 @@ return _device; }; this.getEngine = function () { - var _engine = {}; - _engine[NAME] = undefined; - _engine[VERSION] = undefined; + var _engine = new UAEngine(); rgxMapper.call(_engine, _ua, _rgxmap.engine); return _engine; }; this.getOS = function () { - var _os = {}; - _os[NAME] = undefined; - _os[VERSION] = undefined; + var _os = new UAOS(); rgxMapper.call(_os, _ua, _rgxmap.os); if (!_os[NAME] && _uach && _uach.platform != 'Unknown') { - _os[NAME] = _uach.platform.replace(/chrome/i, 'Chromium').replace(/mac/i, 'Mac '); + _os[NAME] = _uach.platform + .replace(/chrome os/i, 'Chromium OS') + .replace(/macos/i, 'Mac OS'); // backward compatibility } return _os; }; this.getResult = function () { - return { - ua : this.getUA(), - browser : this.getBrowser(), - engine : this.getEngine(), - os : this.getOS(), - device : this.getDevice(), - cpu : this.getCPU() - }; + return new UAResult(this); }; this.getUA = function () { return _ua; @@ -851,7 +906,7 @@ }; this.setUA(_ua); return this; - }; + } UAParser.VERSION = LIBVERSION; UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); diff --git a/test/test.js b/test/test.js index e1fd175d2..18ec18904 100644 --- a/test/test.js +++ b/test/test.js @@ -79,7 +79,7 @@ for (var i in methods) { describe('Returns', function () { it('getResult() should returns JSON', function(done) { - assert.deepStrictEqual(new UAParser('').getResult(), + assert.deepEqual(new UAParser('').getResult(), { ua : '', browser: { name: undefined, version: undefined, major: undefined }, @@ -166,3 +166,99 @@ describe('Testing regexes', function () { }); }); }); + + +describe('is() utility method', function () { + let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); + + it('Should match full name', function () { + assert.strictEqual(uap.getBrowser().name, "IEMobile"); + assert.strictEqual(uap.getBrowser().is("IEMobile"), true); + assert.strictEqual(uap.getBrowser().is("IE"), false); + + it('Should ignore spaces and "Browser" suffix', function () { + assert.strictEqual(uap.getBrowser().is("I EMo b i l e Browser"), true); + }); + }); + + it('Should ignore case', function () { + assert.strictEqual(uap.getEngine().name, "Trident"); + assert.strictEqual(uap.getEngine().is("tRiDeNt"), true); + }); + + it('Should get exact name', function () { + assert.strictEqual(uap.getOS().name, "Windows Phone"); + assert.strictEqual(uap.getOS().is("Windows Phone"), true); + assert.strictEqual(uap.getOS().is("Windows Phone OS"), true); + assert.strictEqual(uap.getOS().is("Windows Mobile"), false); + assert.strictEqual(uap.getOS().is("Android"), false); + }); + + it('Should check all device properties', function () { + assert.deepEqual(uap.getDevice(), { + vendor : "Nokia", + model : "Lumia 635", + type : "mobile" + }); + assert.strictEqual(uap.getDevice().is("Nokia"), true); + assert.strictEqual(uap.getDevice().is("Lumia 635"), true); + assert.strictEqual(uap.getDevice().is("mobile"), true); + + assert.strictEqual(uap.getResult().device.is("Nokia"), true); + + uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15"); + assert.strictEqual(uap.getDevice().is("undefined"), true); + }); + + it('Should get result after reassignment', function () { + uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); + assert.strictEqual(uap.getOS().name, "Mac OS"); + assert.strictEqual(uap.getOS().is("Mac OS"), true); + assert.strictEqual(uap.getOS().is("M ac"), false); + assert.strictEqual(uap.getOS().is("macOS"), true); + assert.strictEqual(uap.getOS().is("mac OS"), true); + assert.strictEqual(uap.getOS().is("M a c "), false); + assert.strictEqual(uap.getOS().is("Mac OS OS"), false); + assert.strictEqual(uap.getOS().is("Mac OS X"), false); + + assert.strictEqual(uap.getBrowser().is("Chrome"), true); + assert.strictEqual(uap.getEngine().is("Blink"), true); + }); + + it('Should accept arch equivalent name', function () { + uap.setUA("Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0"); + assert.strictEqual(uap.getCPU().architecture, "ia32"); + assert.strictEqual(uap.getCPU().is("ia32"), true); + assert.strictEqual(uap.getCPU().is("x86"), false); + + uap.setUA("Opera/9.80 (X11; Linux x86_64; U; Linux Mint; en) Presto/2.2.15 Version/10.10"); + assert.strictEqual(uap.getCPU().architecture, "amd64"); + assert.strictEqual(uap.getCPU().is("amd64"), true); + assert.strictEqual(uap.getCPU().is("x86-64"), false); + assert.strictEqual(uap.getCPU().is("x64"), false); + }); +}); + +describe('toString() utility method', function () { + let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); + assert.strictEqual(uap.getBrowser().name, "IEMobile"); + assert.strictEqual(uap.getBrowser().version, "11.0"); + assert.strictEqual(uap.getBrowser().major, "11"); + assert.strictEqual(uap.getBrowser().toString(), "IEMobile 11.0"); + + assert.strictEqual(uap.getCPU().architecture, "arm"); + assert.strictEqual(uap.getCPU().toString(), "arm"); + + assert.strictEqual(uap.getDevice().vendor, "Nokia"); + assert.strictEqual(uap.getDevice().model, "Lumia 635"); + assert.strictEqual(uap.getDevice().type, "mobile"); + assert.strictEqual(uap.getDevice().toString(), "Nokia Lumia 635"); + + assert.strictEqual(uap.getEngine().name, "Trident"); + assert.strictEqual(uap.getEngine().version, "7.0"); + assert.strictEqual(uap.getEngine().toString(), "Trident 7.0"); + + assert.strictEqual(uap.getOS().name, "Windows Phone"); + assert.strictEqual(uap.getOS().version, "8.1"); + assert.strictEqual(uap.getOS().toString(), "Windows Phone 8.1"); +}); \ No newline at end of file From 746ac28f94457f26746f1c54fbd17441d88adaca Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 19 Feb 2023 16:11:07 +0700 Subject: [PATCH 025/388] Readme: update is() & toString() methods explanation --- readme.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index 514e949d9..cfc05c43e 100644 --- a/readme.md +++ b/readme.md @@ -163,6 +163,32 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ... #### * is() utility `since@1.1` ```js +// Is just a shorthand to check whether one of the specified properties has equal value +// so that instead of write it using `==` operator like this: + +let ua = UAParser(); +let device = ua.device; +let os = ua.os; + +if (device.type == "mobile" && os.name != "iOS") {} +if (device.type == "smarttv" || device.vendor == "Samsung") {} + +// we can also write the comparison above into as follow: + +if (device.is("mobile") && !os.is("iOS")) {} +if (device.is("smarttv") || device.is("Samsung")) {} + +/* + Properties will be checked in this particular order: + * browser : name + * cpu : architecture + * device : type, model, vendor + * engine : name + * os : name +*/ + +// Another examples: + let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); uap.getBrowser().name; // "IEMobile" @@ -183,20 +209,43 @@ uap.getResult().device.is("Nokia"); // true uap.getResult().device.model; // "Lumia 635" uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); -uap.getBrowser().is("IEMobile"); // false -uap.getBrowser().is("Chrome"); // true + +let browser = uap.getBrowser(); +browser.is("IEMobile"); // false +browser.is("Chrome"); // true + uap.getResult().browser.is("Edge"); // false +uap.getResult().os.name // "Mac OS" uap.getResult().os.is("Mac OS"); // true uap.getResult().os.version; // "10.6.8" -uap.getEngine().is("Blink"); // true + +let engine = uap.getEngine(); +engine.is("Blink"); // true ``` #### * toString() utility `since@1.1` ```js +// Retrieve full-name values as a string + +/* + Values will be concatenated following this pattern: + * browser : name + version + * cpu : architecture + * device : vendor + model + * engine : name + version + * os : name + version +*/ + +// Usage examples + let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); -uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" } +uap.getDevice(); // { + // vendor: "Nokia", + // model: "Lumia 635", + // type: "mobile" + // } uap.getDevice().toString(); // "Nokia Lumia 635" uap.getResult().os.name; // "Windows Phone" @@ -204,10 +253,15 @@ uap.getResult().os.version; // "8.1" uap.getResult().os.toString(); // "Windows Phone 8.1" uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); -uap.getBrowser().name; // Chrome -uap.getBrowser().version; // 28.0.1500.95 -uap.getBrowser().major; // 28 +uap.getBrowser().name; // "Chrome" +uap.getBrowser().version; // "28.0.1500.95" +uap.getBrowser().major; // "28" uap.getBrowser().toString(); // "Chrome 28.0.1500.95" + +let engine = uap.getEngine(); +engine.name; // "Blink" +engine.version; // "28.0.1500.95" +engine.toString(); // "Blink 28.0.1500.95" ``` ## Extending Regex From f18516c9c8b858d8c10405f74b0403a53db5ef1f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 19 Feb 2023 16:43:59 +0700 Subject: [PATCH 026/388] Make sure all properties are "undefined" for is("undefined") to be true --- src/ua-parser.js | 9 +++++++-- test/test.js | 14 ++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index cb60dfcee..18ac0f6f1 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -787,12 +787,17 @@ this.rgxIs = propIs[1]; } UAItem.prototype.is = function (strCheck) { + var is = false; for (var i in this.propIs) { if (sanitize(this[this.propIs[i]], this.rgxIs) == sanitize(strCheck, this.rgxIs)) { - return true; + is = true; + if (strCheck != UNDEF_TYPE) break; + } else if (strCheck == UNDEF_TYPE && is) { + is = !is; + break; } } - return false; + return is; }; UAItem.prototype.toString = function () { var str = ''; diff --git a/test/test.js b/test/test.js index a85e9daad..622df973b 100644 --- a/test/test.js +++ b/test/test.js @@ -205,9 +205,6 @@ describe('is() utility method', function () { assert.strictEqual(uap.getDevice().is("mobile"), true); assert.strictEqual(uap.getResult().device.is("Nokia"), true); - - uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15"); - assert.strictEqual(uap.getDevice().is("undefined"), true); }); it('Should get result after reassignment', function () { @@ -225,7 +222,16 @@ describe('is() utility method', function () { assert.strictEqual(uap.getEngine().is("Blink"), true); }); - it('Should accept arch equivalent name', function () { + it('Should refrain from "undefined" until all properties are checked', function () { + assert.strictEqual(uap.getDevice().is("undefined"), false); + assert.strictEqual(uap.getDevice().is("Apple"), true); + + uap.setUA(""); + assert.strictEqual(uap.getDevice().is("undefined"), true); + }); + + //it('Should accept arch equivalent name', function () { + it('Should accept exact arch name', function () { uap.setUA("Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0"); assert.strictEqual(uap.getCPU().architecture, "ia32"); assert.strictEqual(uap.getCPU().is("ia32"), true); From d03c74d6b7b7fb0da3a064337116c7b52579cc60 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 21 Feb 2023 22:12:30 +0700 Subject: [PATCH 027/388] Insert sponsorship content from 51degrees --- images/51degrees.svg | 19 +++++++++++++++++++ readme.md | 24 ++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 images/51degrees.svg diff --git a/images/51degrees.svg b/images/51degrees.svg new file mode 100644 index 000000000..c3393f646 --- /dev/null +++ b/images/51degrees.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/readme.md b/readme.md index cfc05c43e..4bae24fa3 100644 --- a/readme.md +++ b/readme.md @@ -18,6 +18,25 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro * Demo : https://faisalman.github.io/ua-parser-js * Source : https://github.com/faisalman/ua-parser-js +*** + +### From Our Sponsors: + + + + + + + + + + + + +
51degrees↗ @51degrees/ua-parser-js

UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.

This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).

↗ Visit 51Degrees UAParser to get started.

+ +--- + # Documentation ### UAParser([user-agent:string][,extensions:object]) @@ -427,9 +446,10 @@ $('body').addClass('ua-browser-' + $.ua.browser.name + ' ua-devicetype-' + $.ua. # Development -## Sponsors +## Backers & Sponsors - + + From 03b0a5afa60d472dfff160185c493cb94f1e0298 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 22 Feb 2023 20:52:27 +0700 Subject: [PATCH 028/388] Fix #498 - Detect Brave Browser by checking navigator.brave https://github.com/brave/brave-browser/issues/10165#issuecomment-641128278 --- src/ua-parser.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 18ac0f6f1..71a5c9208 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -860,8 +860,9 @@ return new UAParser(ua, extensions).getResult(); } - var _ua = ua || ((typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY); - var _uach = (typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgentData) ? window.navigator.userAgentData : undefined; + var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined; + var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : EMPTY); + var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined; var _rgxmap = extensions ? extend(regexes, extensions) : regexes; // public methods @@ -869,6 +870,10 @@ var _browser = new UABrowser(); rgxMapper.call(_browser, _ua, _rgxmap.browser); _browser[MAJOR] = majorize(_browser[VERSION]); + // Brave-specific detection + if (_navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) { + _browser[NAME] = 'Brave'; + } return _browser; }; this.getCPU = function () { From 5ea9f0ec16fcbe53f59e5d3a95d237e179a0539c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 24 Feb 2023 14:25:30 +0700 Subject: [PATCH 029/388] Fix #387 #554 - Detect iPadOS 13 https://stackoverflow.com/questions/9038625/detect-if-device-is-ios --- src/ua-parser.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 71a5c9208..a3a3ad414 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -58,7 +58,9 @@ SONY = 'Sony', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', - FACEBOOK = 'Facebook'; + FACEBOOK = 'Facebook', + CHROMIUM_OS = 'Chromium OS', + MAC_OS = 'Mac OS'; /////////// // Helper @@ -721,7 +723,7 @@ ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS - ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [ + ], [[NAME, MAC_OS], [VERSION, /_/g, '.']], [ // Mobile OSes /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS @@ -745,7 +747,7 @@ /crkey\/([\d\.]+)/i // Google Chromecast ], [VERSION, [NAME, CHROME+'cast']], [ /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS - ], [[NAME, 'Chromium OS'], VERSION],[ + ], [[NAME, CHROMIUM_OS], VERSION],[ // Smart TVs /panasonic;(viera)/i, // Panasonic Viera @@ -887,6 +889,11 @@ if (!_device[TYPE] && _uach && _uach.mobile) { _device[TYPE] = MOBILE; } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (_device[MODEL] == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) { + _device[MODEL] = 'iPad'; + _device[TYPE] = TABLET; + } return _device; }; this.getEngine = function () { @@ -899,8 +906,8 @@ rgxMapper.call(_os, _ua, _rgxmap.os); if (!_os[NAME] && _uach && _uach.platform != 'Unknown') { _os[NAME] = _uach.platform - .replace(/chrome os/i, 'Chromium OS') - .replace(/macos/i, 'Mac OS'); // backward compatibility + .replace(/chrome os/i, CHROMIUM_OS) + .replace(/macos/i, MAC_OS); // backward compatibility } return _os; }; From 18730452f2b40099710613ecbe188d98f4bfad19 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 25 Feb 2023 11:41:19 +0700 Subject: [PATCH 030/388] Add new device: Apple Watch, new os: watchOS --- readme.md | 15 ++++++++------- src/ua-parser.js | 4 ++++ test/device-test.json | 9 +++++++++ test/os-test.json | 27 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index 4bae24fa3..fdeccfd4d 100644 --- a/readme.md +++ b/readme.md @@ -159,7 +159,7 @@ Linpus, Linspire,Linux, Mac OS, Maemo, Mageia, Mandriva, Manjaro, MeeGo, Minix, Mint, Morph OS, NetBSD, NetRange, NetTV, Nintendo, OpenBSD, OpenVMS, OS/2, Palm, PC-BSD, PCLinuxOS, Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS, RISC OS, Sabayon, Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen, -Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ... +Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwalk, ... # 'os.version' determined dynamically ``` @@ -182,7 +182,7 @@ Ubuntu, Unix, VectorLinux, Viera, WebOS, Windows [Phone/Mobile], Zenwalk, ... #### * is() utility `since@1.1` ```js -// Is just a shorthand to check whether one of the specified properties has equal value +// Is just a shorthand to check whether specified item has a property with equals value (case-insensitive) // so that instead of write it using `==` operator like this: let ua = UAParser(); @@ -198,7 +198,7 @@ if (device.is("mobile") && !os.is("iOS")) {} if (device.is("smarttv") || device.is("Samsung")) {} /* - Properties will be checked in this particular order: + Each properties will be checked in this particular order: * browser : name * cpu : architecture * device : type, model, vendor @@ -220,10 +220,11 @@ uap.getOS().is("Windows Phone"); // true uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" } uap.getResult().device; // { vendor: "Nokia", model: "Lumia 635", type: "mobile" } -uap.getDevice().is("mobile"); // true -uap.getDevice().is("Lumia 635"); // true -uap.getDevice().is("Nokia"); // true -uap.getDevice().is("iPhone"); // false +let device = uap.getDevice(); +device.is("mobile"); // true +device.is("Lumia 635"); // true +device.is("Nokia"); // true +device.is("iPhone"); // false uap.getResult().device.is("Nokia"); // true uap.getResult().device.model; // "Lumia 635" diff --git a/src/ua-parser.js b/src/ua-parser.js index a3a3ad414..f03bfc525 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -656,6 +656,8 @@ /((pebble))app/i // Pebble ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ + /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch + ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ /droid.+; (glass) \d/i // Google Glass ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i @@ -742,6 +744,8 @@ /web0s;.+rt(tv)/i, /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS ], [VERSION, [NAME, 'webOS']], [ + /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS + ], [VERSION, [NAME, 'watchOS']], [ // Google Chromecast /crkey\/([\d\.]+)/i // Google Chromecast diff --git a/test/device-test.json b/test/device-test.json index f042a17db..c40361f13 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -763,6 +763,15 @@ "type": "undefined" } }, + { + "desc": "Apple Watch", + "ua": "atc/1.0 watchOS/7.3.3 model/Watch4,2 hwp/t8006 build/18S830 (6; dt:191)", + "expect": { + "vendor": "Apple", + "model": "watch", + "type": "wearable" + } + }, { "desc": "iPad using UCBrowser", "ua": "Mozilla/5.0 (iPad; U; CPU OS 11_2 like Mac OS X; zh-CN; iPad5,3) AppleWebKit/534.46 (KHTML, like Gecko) UCBrowser/3.0.1.776 U3/ Mobile/10A403 Safari/7543.48.3", diff --git a/test/os-test.json b/test/os-test.json index 20670158d..d97c7ffce 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -764,6 +764,33 @@ "version" : "undefined" } }, + { + "desc" : "watchOS", + "ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]", + "expect" : + { + "name" : "watchOS", + "version" : "8.4" + } + }, + { + "desc" : "watchOS", + "ua" : "atc/1.0 watchOS/7.4.1 model/Watch3,3 hwp/t8004 build/18T201 (6; dt:155)", + "expect" : + { + "name" : "watchOS", + "version" : "7.4.1" + } + }, + { + "desc" : "watchOS", + "ua" : "Watch4,3/5.3.8 (16U680)", + "expect" : + { + "name" : "watchOS", + "version" : "5.3.8" + } + }, { "desc" : "Mac OS on PowerPC", "ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)", From f1449a8202782d13dc5f437617d07143d0934ef0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 26 Feb 2023 18:27:55 +0700 Subject: [PATCH 031/388] Refactor --- src/ua-parser.js | 219 +++++++++++++++++++++++++++++------------------ test/test.js | 4 +- 2 files changed, 137 insertions(+), 86 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index f03bfc525..188fbcbc9 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -56,8 +56,10 @@ SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', + SWISS = 'Swiss', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', + ZTE = 'ZTE', FACEBOOK = 'Facebook', CHROMIUM_OS = 'Chromium OS', MAC_OS = 'Mac OS'; @@ -87,15 +89,12 @@ has = function (str1, str2) { return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, - lowerize = function (str) { - return typeof(str) === STR_TYPE ? str.toLowerCase() : UNDEF_TYPE; + lowerize = function (str, rgx) { + return typeof(str) === STR_TYPE ? str.toLowerCase().replace((rgx ? new RegExp(rgx, 'i') : EMPTY), EMPTY) : str; }, majorize = function (version) { return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] : undefined; }, - sanitize = function (str, rgx) { - return rgx ? lowerize(str).replace(rgx, EMPTY) : lowerize(str); - }, trim = function (str, len) { if (typeof(str) === STR_TYPE) { str = str.replace(/^\s\s*/, EMPTY); @@ -346,7 +345,7 @@ ], [NAME, VERSION], [ /(cobalt)\/([\w\.]+)/i // Cobalt - ], [NAME, [VERSION, /master.|lts./, ""]] + ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] ], cpu : [[ @@ -566,13 +565,13 @@ /\b(tm\d{3}\w+) b/i ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ /\b(k88) b/i // ZTE K Series Tablet - ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, ZTE], [TYPE, TABLET]], [ /\b(nx\d{3}j) b/i // ZTE Nubia - ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ + ], [MODEL, [VENDOR, ZTE], [TYPE, MOBILE]], [ /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile - ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ + ], [MODEL, [VENDOR, SWISS], [TYPE, MOBILE]], [ /\b(zur\d{3}) b/i // Swiss ZUR Tablet - ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, SWISS], [TYPE, TABLET]], [ /\b((zeki)?tb.*\b) b/i // Zeki Tablets ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ /\b([yr]\d{2}) b/i, @@ -600,7 +599,7 @@ ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /(kin\.[onetw]{3})/i // Microsoft Kin ], [[MODEL, /\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [ - /droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra + /droid.+; ([c6]+|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra ], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [ /droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [ @@ -624,7 +623,7 @@ /\(dtv[\);].+(aquos)/i, /(aquos-tv[\w ]+)\)/i // Sharp ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ - /(bravia[\w ]+)( bui|\))/i // Sony + /(bravia[\w ]+)( bui|\))/i // Sony ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ /(mitv-\w{5}) bui/i // Xiaomi ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ @@ -641,11 +640,11 @@ /////////////////// /(ouya)/i, // Ouya - /(nintendo) ([wids3utch]+)/i // Nintendo + /(nintendo) (\w+)/i // Nintendo ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ /droid.+; (shield) bui/i // Nvidia ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [ - /(playstation [345portablevi]+)/i // Playstation + /(playstation \w+)/i // Playstation ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [ /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [ @@ -759,7 +758,7 @@ /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV // Console - /(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation + /(nintendo|playstation) (\w+)/i, // Nintendo/Playstation /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) // Other @@ -787,73 +786,117 @@ // Constructor //////////////// - function UAItem (propToString, propIs) { - this.propToString = propToString; - this.propIs = propIs[0]; - this.rgxIs = propIs[1]; - } - UAItem.prototype.is = function (strCheck) { - var is = false; - for (var i in this.propIs) { - if (sanitize(this[this.propIs[i]], this.rgxIs) == sanitize(strCheck, this.rgxIs)) { - is = true; - if (strCheck != UNDEF_TYPE) break; - } else if (strCheck == UNDEF_TYPE && is) { - is = !is; - break; - } + function UAItem () {} + UAItem.prototype.get = function (prop) { + if (!prop) { + return this.data; } - return is; + return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; }; - UAItem.prototype.toString = function () { - var str = ''; - for (var i in this.propToString) { - if (typeof(this[this.propToString[i]]) !== UNDEF_TYPE) { - str += (str ? ' ' : '') + this[this.propToString[i]]; - } - } - return str ? str : UNDEF_TYPE; + UAItem.prototype.parse = function (ua, rgxmap) { + rgxMapper.call(this.data, ua, rgxmap); + }; + UAItem.prototype.set = function (prop, val) { + this.data[prop] = val; + }; + UAItem.prototype.then = function (callback) { + return callback(this.data); + }; + + var createUAData = function (data) { + return (function () { + var propIs = data.propIs; + var ignoreIs = data.ignoreIs; + var propToStr = data.propToStr; + var UAData = function () { + for (var i in data.props) { + this[data.props[i]] = undefined; + } + }; + UAData.prototype.is = function (strToCheck) { + var is = false; + for (var i in propIs) { + if (lowerize(this[propIs[i]], ignoreIs) === lowerize(strToCheck, ignoreIs)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; + } + } + return is; + }; + UAData.prototype.toString = function () { + var str = EMPTY; + for (var i in propToStr) { + if (typeof(this[propToStr[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[propToStr[i]]; + } + } + return str ? str : UNDEF_TYPE; + }; + return new UAData(); + })(data); }; function UABrowser () { - this[NAME] = undefined; - this[VERSION] = undefined; - this[MAJOR] = undefined; + this.data = createUAData({ + props : [NAME, VERSION, MAJOR], + propIs : [NAME], + ignoreIs : ' ?browser$', + propToStr : [NAME, VERSION] + }); } - UABrowser.prototype = new UAItem([NAME, VERSION], [[NAME], /\s?browser$/i]); + UABrowser.prototype = new UAItem(); function UACPU () { - this[ARCHITECTURE] = undefined; + this.data = createUAData({ + props : [ARCHITECTURE], + propIs : [ARCHITECTURE], + propToStr : [ARCHITECTURE] + }); } - UACPU.prototype = new UAItem([ARCHITECTURE], [[ARCHITECTURE]]); + UACPU.prototype = new UAItem(); function UADevice () { - this[VENDOR] = undefined; - this[MODEL] = undefined; - this[TYPE] = undefined; + this.data = createUAData({ + props : [TYPE, MODEL, VENDOR], + propIs : [TYPE, MODEL, VENDOR], + propToStr : [VENDOR, MODEL] + }); } - UADevice.prototype = new UAItem([VENDOR, MODEL], [[TYPE, MODEL, VENDOR]]); + UADevice.prototype = new UAItem(); function UAEngine () { - this[NAME] = undefined; - this[VERSION] = undefined; + this.data = createUAData({ + props : [NAME, VERSION], + propIs : [NAME], + propToStr : [NAME, VERSION] + }); } - UAEngine.prototype = new UAItem([NAME, VERSION], [[NAME]]); + UAEngine.prototype = new UAItem(); function UAOS () { - this[NAME] = undefined; - this[VERSION] = undefined; + this.data = createUAData({ + props : [NAME, VERSION], + propIs : [NAME], + ignoreIs : ' ?os$', + propToStr : [NAME, VERSION] + }); } - UAOS.prototype = new UAItem([NAME, VERSION], [[NAME], /\s?os$/i]); - - function UAResult (uap) { - this.ua = uap.getUA(); - this.browser = uap.getBrowser(); - this.cpu = uap.getCPU(); - this.device = uap.getDevice(); - this.engine = uap.getEngine(); - this.os = uap.getOS(); + UAOS.prototype = new UAItem(); + + function UAResult () { + this.data = { + ua : '', + browser : undefined, + cpu : undefined, + device : undefined, + engine : undefined, + os : undefined + }; } + UAResult.prototype = new UAItem(); function UAParser (ua, extensions) { @@ -861,7 +904,6 @@ extensions = ua; ua = undefined; } - if (!(this instanceof UAParser)) { return new UAParser(ua, extensions).getResult(); } @@ -874,49 +916,56 @@ // public methods this.getBrowser = function () { var _browser = new UABrowser(); - rgxMapper.call(_browser, _ua, _rgxmap.browser); - _browser[MAJOR] = majorize(_browser[VERSION]); + _browser.parse(_ua, _rgxmap.browser); + _browser.set(MAJOR, majorize(_browser.get(VERSION))); // Brave-specific detection if (_navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) { - _browser[NAME] = 'Brave'; + _browser.set(NAME, 'Brave'); } - return _browser; + return _browser.get(); }; this.getCPU = function () { var _cpu = new UACPU(); - rgxMapper.call(_cpu, _ua, _rgxmap.cpu); - return _cpu; + _cpu.parse(_ua, _rgxmap.cpu); + return _cpu.get(); }; this.getDevice = function () { var _device = new UADevice(); - rgxMapper.call(_device, _ua, _rgxmap.device); - if (!_device[TYPE] && _uach && _uach.mobile) { - _device[TYPE] = MOBILE; + _device.parse(_ua, _rgxmap.device); + if (!_device.get(TYPE) && _uach && _uach.mobile) { + _device.set(TYPE, MOBILE); } // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (_device[MODEL] == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) { - _device[MODEL] = 'iPad'; - _device[TYPE] = TABLET; + if (_device.get(NAME) == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) { + _device.set(MODEL, 'iPad'); + _device.set(TYPE, TABLET); } - return _device; + return _device.get(); }; this.getEngine = function () { var _engine = new UAEngine(); - rgxMapper.call(_engine, _ua, _rgxmap.engine); - return _engine; + _engine.parse(_ua, _rgxmap.engine); + return _engine.get(); }; this.getOS = function () { var _os = new UAOS(); - rgxMapper.call(_os, _ua, _rgxmap.os); - if (!_os[NAME] && _uach && _uach.platform != 'Unknown') { - _os[NAME] = _uach.platform + _os.parse(_ua, _rgxmap.os); + if (!_os.get(NAME) && _uach && _uach.platform != 'Unknown') { + _os.set(NAME, _uach.platform .replace(/chrome os/i, CHROMIUM_OS) - .replace(/macos/i, MAC_OS); // backward compatibility + .replace(/macos/i, MAC_OS)); // backward compatibility } - return _os; + return _os.get(); }; this.getResult = function () { - return new UAResult(this); + var _result = new UAResult(); + _result.set('ua', _ua); + _result.set('browser', this.getBrowser()); + _result.set('cpu', this.getCPU()); + _result.set('device', this.getDevice()); + _result.set('engine', this.getEngine()); + _result.set('os', this.getOS()); + return _result.get(); }; this.getUA = function () { return _ua; diff --git a/test/test.js b/test/test.js index 622df973b..a906e2ce2 100644 --- a/test/test.js +++ b/test/test.js @@ -45,7 +45,7 @@ var methods = [ describe('UAParser()', function () { var ua = 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6'; - assert.deepStrictEqual(UAParser(ua), new UAParser().setUA(ua).getResult()); + assert.deepEqual(UAParser(ua), new UAParser().setUA(ua).getResult()); }); describe('UAParser() constructor does not throw with undefined ua argument', function () { @@ -227,7 +227,9 @@ describe('is() utility method', function () { assert.strictEqual(uap.getDevice().is("Apple"), true); uap.setUA(""); + assert.strictEqual(uap.getDevice().model, undefined); assert.strictEqual(uap.getDevice().is("undefined"), true); + assert.strictEqual(uap.getDevice().is(undefined), true); }); //it('Should accept arch equivalent name', function () { From c6b4c867d54048def80a358b571041ac308cd496 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 28 Feb 2023 00:10:56 +0700 Subject: [PATCH 032/388] Sponsorship content revision --- readme.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index fdeccfd4d..fd32a7fb7 100644 --- a/readme.md +++ b/readme.md @@ -26,11 +26,16 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro -51degrees +51degrees ↗ @51degrees/ua-parser-js -

UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.

This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).

↗ Visit 51Degrees UAParser to get started.

+

UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.

This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).

Visit ↗ 51Degrees UAParser to get started.

+ + + + +

On 6 March, we’ll be hosting a demonstration of the 51Degrees UAParser. Register your place for the webinar ↗ here.

From 9ee128ae2b7644176cd693904dcdf01fb8c84701 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 28 Feb 2023 12:25:57 +0700 Subject: [PATCH 033/388] Fix failing CI test, update funding & license year --- .github/FUNDING.yml | 2 +- license.md | 2 +- package.json | 4 ++++ readme.md | 16 ++++++++-------- src/ua-parser.js | 15 +++++++-------- test/test.js | 2 +- 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 3ec50a0bb..b8b83265b 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: faisalman patreon: # Replace with a single Patreon username open_collective: ua-parser-js ko_fi: # Replace with a single Ko-fi username diff --git a/license.md b/license.md index 01d840a46..9ba50e27f 100644 --- a/license.md +++ b/license.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2012-2021 Faisal Salman <> +Copyright (c) 2012-2023 Faisal Salman <> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/package.json b/package.json index 7d9877589..37854dce2 100644 --- a/package.json +++ b/package.json @@ -197,6 +197,10 @@ { "type": "paypal", "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" } ] } diff --git a/readme.md b/readme.md index fd32a7fb7..acf44a1e9 100644 --- a/readme.md +++ b/readme.md @@ -90,10 +90,10 @@ The methods are self explanatory, here's a small overview on all the available m --- -* `getResult() : UAResult` +* `getResult()` * returns `{ ua: '', browser: UABrowser {}, cpu: UACPU {}, device: UADevice {}, engine: UAEngine {}, os: UAOS {} }` -* `getBrowser() : UABrowser` +* `getBrowser()` * returns `{ name: '', version: '' }` ```sh @@ -117,7 +117,7 @@ Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser... # 'browser.version' determined dynamically ``` -* `getDevice() : UADevice` +* `getDevice()` * returns `{ model: '', type: '', vendor: '' }` ```sh @@ -141,7 +141,7 @@ Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE # 'device.model' determined dynamically ``` -* `getEngine() : UAEngine` +* `getEngine()` * returns `{ name: '', version: '' }` ```sh @@ -152,7 +152,7 @@ NetSurf, Presto, Tasman, Trident, w3m, WebKit # 'engine.version' determined dynamically ``` -* `getOS() : UAOS` +* `getOS()` * returns `{ name: '', version: '' }` ```sh @@ -169,7 +169,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal # 'os.version' determined dynamically ``` -* `getCPU() : UACPU` +* `getCPU()` * returns `{ architecture: '' }` ```sh @@ -177,7 +177,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal 68k, amd64, arm[64/hf], avr, ia[32/64], irix[64], mips[64], pa-risc, ppc, sparc[64] ``` -* `getUA() : string` +* `getUA()` * returns UA string of current instance * `setUA(uastring)` @@ -479,7 +479,7 @@ Made with [contributors-img](https://contrib.rocks). MIT License -Copyright (c) 2012-2021 Faisal Salman <> +Copyright (c) 2012-2023 Faisal Salman <> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/ua-parser.js b/src/ua-parser.js index 188fbcbc9..a219a3877 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////////// /* UAParser.js v1.1.34 - Copyright © 2012-2021 Faisal Salman + Copyright © 2012-2023 Faisal Salman MIT License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. @@ -802,8 +802,7 @@ UAItem.prototype.then = function (callback) { return callback(this.data); }; - - var createUAData = function (data) { + UAItem.createUAData = function (data) { return (function () { var propIs = data.propIs; var ignoreIs = data.ignoreIs; @@ -840,7 +839,7 @@ }; function UABrowser () { - this.data = createUAData({ + this.data = UAItem.createUAData({ props : [NAME, VERSION, MAJOR], propIs : [NAME], ignoreIs : ' ?browser$', @@ -850,7 +849,7 @@ UABrowser.prototype = new UAItem(); function UACPU () { - this.data = createUAData({ + this.data = UAItem.createUAData({ props : [ARCHITECTURE], propIs : [ARCHITECTURE], propToStr : [ARCHITECTURE] @@ -859,7 +858,7 @@ UACPU.prototype = new UAItem(); function UADevice () { - this.data = createUAData({ + this.data = UAItem.createUAData({ props : [TYPE, MODEL, VENDOR], propIs : [TYPE, MODEL, VENDOR], propToStr : [VENDOR, MODEL] @@ -868,7 +867,7 @@ UADevice.prototype = new UAItem(); function UAEngine () { - this.data = createUAData({ + this.data = UAItem.createUAData({ props : [NAME, VERSION], propIs : [NAME], propToStr : [NAME, VERSION] @@ -877,7 +876,7 @@ UAEngine.prototype = new UAItem(); function UAOS () { - this.data = createUAData({ + this.data = UAItem.createUAData({ props : [NAME, VERSION], propIs : [NAME], ignoreIs : ' ?os$', diff --git a/test/test.js b/test/test.js index a906e2ce2..7b4e4a448 100644 --- a/test/test.js +++ b/test/test.js @@ -228,7 +228,7 @@ describe('is() utility method', function () { uap.setUA(""); assert.strictEqual(uap.getDevice().model, undefined); - assert.strictEqual(uap.getDevice().is("undefined"), true); + assert.strictEqual(uap.getDevice().is("undefined"), false); assert.strictEqual(uap.getDevice().is(undefined), true); }); From d99ff741f4d4d254e1e744a49202a858dd49ac18 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 28 Feb 2023 14:03:09 +0700 Subject: [PATCH 034/388] Add new feature: ability to pass req.headers object directly into UAParser --- readme.md | 11 ++-- src/ua-parser.js | 129 ++++++++++++++++++++++++++--------------------- test/test.js | 108 +++++++++++++++++++++++++++++++-------- 3 files changed, 164 insertions(+), 84 deletions(-) diff --git a/readme.md b/readme.md index acf44a1e9..73bfa27db 100644 --- a/readme.md +++ b/readme.md @@ -53,7 +53,7 @@ Usually you can find the user agent in: ## Constructor When you call `UAParser` with the `new` keyword `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string. Like so: -* `new UAParser([user-agent:string][,extensions:object])` +* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])` ```js let parser = new UAParser("user-agent"); // you need to pass the user-agent for nodejs console.log(parser); // {} @@ -70,7 +70,7 @@ console.log(parserResults); ``` When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results. -* `UAParser([user-agent:string][,extensions:object])` +* `UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])` * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` ## Methods @@ -293,16 +293,17 @@ engine.toString(); // "Blink 28.0.1500.95" If you want to detect something that's not currently provided by UAParser.js (eg: bots, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own. -* `UAParser([uastring,] extensions)` +* `UAParser([uastring,] extensions [,headers:object(since@1.1)])` ```js // Example: let myOwnListOfBrowsers = [ - [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION] + [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] ]; let myParser = new UAParser({ browser: myOwnListOfBrowsers }); let myUA = 'Mozilla/5.0 MyBrowser/1.3'; -console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3"} +console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3", major: "1", type : "bot"} +console.log(myParser.getBrowser().is('bot')); // true // Another example: let myOwnListOfDevices = [ diff --git a/src/ua-parser.js b/src/ua-parser.js index a219a3877..7a3dc7a5e 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -37,6 +37,7 @@ SMARTTV = 'smarttv', WEARABLE = 'wearable', EMBEDDED = 'embedded', + USER_AGENT = 'user-agent', UA_MAX_LENGTH = 350; var AMAZON = 'Amazon', @@ -71,11 +72,7 @@ var extend = function (regexes, extensions) { var mergedRegexes = {}; for (var i in regexes) { - if (extensions[i] && extensions[i].length % 2 === 0) { - mergedRegexes[i] = extensions[i].concat(regexes[i]); - } else { - mergedRegexes[i] = regexes[i]; - } + mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; } return mergedRegexes; }, @@ -89,6 +86,11 @@ has = function (str1, str2) { return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, + isExtensions = function (obj) { + for (var prop in obj) { + return /^(browser|cpu|device|engine|os)$/.test(prop); + } + }, lowerize = function (str, rgx) { return typeof(str) === STR_TYPE ? str.toLowerCase().replace((rgx ? new RegExp(rgx, 'i') : EMPTY), EMPTY) : str; }, @@ -803,84 +805,83 @@ return callback(this.data); }; UAItem.createUAData = function (data) { - return (function () { - var propIs = data.propIs; - var ignoreIs = data.ignoreIs; - var propToStr = data.propToStr; - var UAData = function () { - for (var i in data.props) { - this[data.props[i]] = undefined; - } - }; - UAData.prototype.is = function (strToCheck) { - var is = false; - for (var i in propIs) { - if (lowerize(this[propIs[i]], ignoreIs) === lowerize(strToCheck, ignoreIs)) { - is = true; - if (strToCheck != UNDEF_TYPE) break; - } else if (strToCheck == UNDEF_TYPE && is) { - is = !is; - break; - } + var is_ignoreProps = data.is_ignoreProps, + is_ignoreRgx = data.is_ignoreRgx, + toString_props = data.toString_props; + + var UAData = function () { + for (var i in data.init_props) { + this[data.init_props[i]] = undefined; + } + }; + UAData.prototype.is = function (strToCheck) { + var is = false; + for (var i in this) { + if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; } - return is; - }; - UAData.prototype.toString = function () { - var str = EMPTY; - for (var i in propToStr) { - if (typeof(this[propToStr[i]]) !== UNDEF_TYPE) { - str += (str ? ' ' : EMPTY) + this[propToStr[i]]; - } + } + return is; + }; + UAData.prototype.toString = function () { + var str = EMPTY; + for (var i in toString_props) { + if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[toString_props[i]]; } - return str ? str : UNDEF_TYPE; - }; - return new UAData(); - })(data); + } + return str ? str : UNDEF_TYPE; + }; + return new UAData(); }; function UABrowser () { this.data = UAItem.createUAData({ - props : [NAME, VERSION, MAJOR], - propIs : [NAME], - ignoreIs : ' ?browser$', - propToStr : [NAME, VERSION] + init_props : [NAME, VERSION, MAJOR], + is_ignoreProps : [VERSION, MAJOR], + is_ignoreRgx : ' ?browser$', + toString_props : [NAME, VERSION] }); } UABrowser.prototype = new UAItem(); function UACPU () { this.data = UAItem.createUAData({ - props : [ARCHITECTURE], - propIs : [ARCHITECTURE], - propToStr : [ARCHITECTURE] + init_props : [ARCHITECTURE], + is_ignoreProps : [], + toString_props : [ARCHITECTURE] }); } UACPU.prototype = new UAItem(); function UADevice () { this.data = UAItem.createUAData({ - props : [TYPE, MODEL, VENDOR], - propIs : [TYPE, MODEL, VENDOR], - propToStr : [VENDOR, MODEL] + init_props : [TYPE, MODEL, VENDOR], + is_ignoreProps : [], + toString_props : [VENDOR, MODEL] }); } UADevice.prototype = new UAItem(); function UAEngine () { this.data = UAItem.createUAData({ - props : [NAME, VERSION], - propIs : [NAME], - propToStr : [NAME, VERSION] + init_props : [NAME, VERSION], + is_ignoreProps : [VERSION], + toString_props : [NAME, VERSION] }); } UAEngine.prototype = new UAItem(); function UAOS () { this.data = UAItem.createUAData({ - props : [NAME, VERSION], - propIs : [NAME], - ignoreIs : ' ?os$', - propToStr : [NAME, VERSION] + init_props : [NAME, VERSION], + is_ignoreProps : [VERSION], + is_ignoreRgx : ' ?os$', + toString_props : [NAME, VERSION] }); } UAOS.prototype = new UAItem(); @@ -897,18 +898,30 @@ } UAResult.prototype = new UAItem(); - function UAParser (ua, extensions) { + function UAParser (ua, extensions, headers) { if (typeof ua === OBJ_TYPE) { - extensions = ua; + if (isExtensions(ua)) { + if (typeof extensions === OBJ_TYPE) { + headers = extensions; // case UAParser(extensions, headers) + } + extensions = ua; // case UAParser(extensions) + } else { + headers = ua; // case UAParser(headers) + } ua = undefined; + } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { + headers = extensions; // case UAParser(ua, headers) + extensions = undefined; } + if (!(this instanceof UAParser)) { - return new UAParser(ua, extensions).getResult(); + return new UAParser(ua, extensions, headers).getResult(); } - var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined; - var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : EMPTY); + + // _ua = user-supplied string || window.navigator.userAgent || user-agent header || empty + var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : (!ua && headers && headers[USER_AGENT] ? headers[USER_AGENT] : EMPTY)); var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined; var _rgxmap = extensions ? extend(regexes, extensions) : regexes; diff --git a/test/test.js b/test/test.js index 7b4e4a448..339f3c309 100644 --- a/test/test.js +++ b/test/test.js @@ -106,6 +106,25 @@ describe('Extending Regex', function () { parser2.setUA(uaString); assert.strictEqual(parser2.getBrowser().name, 'MyOwnBrowser'); assert.strictEqual(parser1.getBrowser().version, '1.3'); + + let myOwnListOfBrowsers = [ + [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] + ]; + let myParser = new UAParser({ browser: myOwnListOfBrowsers }); + let myUA = 'Mozilla/5.0 MyBrowser/1.3'; + assert.deepEqual(myParser.setUA(myUA).getBrowser(), {name: "MyBrowser", version: "1.3", major: "1", type : "bot"}); + assert.strictEqual(myParser.getBrowser().is('bot'), true); + + let myOwnListOfDevices = [ + [/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]], + [/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]] + ]; + let myParser2 = new UAParser({ + browser: myOwnListOfBrowsers, + device: myOwnListOfDevices + }); + let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; + assert.deepEqual(myParser2.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}); }); describe('User-agent length', function () { @@ -248,25 +267,72 @@ describe('is() utility method', function () { }); describe('toString() utility method', function () { - let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); - assert.strictEqual(uap.getBrowser().name, "IEMobile"); - assert.strictEqual(uap.getBrowser().version, "11.0"); - assert.strictEqual(uap.getBrowser().major, "11"); - assert.strictEqual(uap.getBrowser().toString(), "IEMobile 11.0"); - - assert.strictEqual(uap.getCPU().architecture, "arm"); - assert.strictEqual(uap.getCPU().toString(), "arm"); - - assert.strictEqual(uap.getDevice().vendor, "Nokia"); - assert.strictEqual(uap.getDevice().model, "Lumia 635"); - assert.strictEqual(uap.getDevice().type, "mobile"); - assert.strictEqual(uap.getDevice().toString(), "Nokia Lumia 635"); - - assert.strictEqual(uap.getEngine().name, "Trident"); - assert.strictEqual(uap.getEngine().version, "7.0"); - assert.strictEqual(uap.getEngine().toString(), "Trident 7.0"); - - assert.strictEqual(uap.getOS().name, "Windows Phone"); - assert.strictEqual(uap.getOS().version, "8.1"); - assert.strictEqual(uap.getOS().toString(), "Windows Phone 8.1"); + it('Should return full name', function () { + let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); + assert.strictEqual(uap.getBrowser().name, "IEMobile"); + assert.strictEqual(uap.getBrowser().version, "11.0"); + assert.strictEqual(uap.getBrowser().major, "11"); + assert.strictEqual(uap.getBrowser().toString(), "IEMobile 11.0"); + + assert.strictEqual(uap.getCPU().architecture, "arm"); + assert.strictEqual(uap.getCPU().toString(), "arm"); + + assert.strictEqual(uap.getDevice().vendor, "Nokia"); + assert.strictEqual(uap.getDevice().model, "Lumia 635"); + assert.strictEqual(uap.getDevice().type, "mobile"); + assert.strictEqual(uap.getDevice().toString(), "Nokia Lumia 635"); + + assert.strictEqual(uap.getEngine().name, "Trident"); + assert.strictEqual(uap.getEngine().version, "7.0"); + assert.strictEqual(uap.getEngine().toString(), "Trident 7.0"); + + assert.strictEqual(uap.getOS().name, "Windows Phone"); + assert.strictEqual(uap.getOS().version, "8.1"); + assert.strictEqual(uap.getOS().toString(), "Windows Phone 8.1"); + }); +}); + +describe('Read user-agent data from req.headers', function () { + const ua = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)'; + const ext = { + engine : [ + [/(msie)/i], [[UAParser.ENGINE.NAME, 'Custom Browser 1']], + [/(edge)/i], [[UAParser.ENGINE.NAME, 'Custom Browser 2']] + ] + }; + const req = { + headers : { + 'user-agent' : 'Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36 Edge/12.0' + } + }; + + it('Can be called with UAParser(ua)', function () { + let engine = UAParser(ua).engine; + assert.strictEqual(engine.name, "Trident"); + }); + + it('Can be called with UAParser(ua, extensions)', function () { + let engine = UAParser(ua, ext).engine; + assert.strictEqual(engine.name, "Custom Browser 1"); + }); + + it('Can be called with UAParser(ua, extensions, headers)', function () { + let engine = UAParser(ua, ext, req.headers).engine; + assert.strictEqual(engine.name, "Custom Browser 1"); + }); + + it('Can be called with UAParser(ua, headers)', function () { + let engine = UAParser(ua, req.headers).engine; + assert.strictEqual(engine.name, "Trident"); + }); + + it('Can be called with UAParser(extensions, headers)', function () { + let engine = UAParser(ext, req.headers).engine; + assert.strictEqual(engine.name, "Custom Browser 2"); + }); + + it('Can be called with UAParser(headers)', function () { + let engine = UAParser(req.headers).engine; + assert.strictEqual(engine.name, "EdgeHTML"); + }); }); \ No newline at end of file From 3c3c03ceeba9920437533dcf1d72276acac091a3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 4 Mar 2023 22:33:13 +0700 Subject: [PATCH 035/388] Add new feature: read Client Hints data from HTTP Headers #408 #566 #588 --- readme.md | 37 +++-- src/ua-parser.js | 382 ++++++++++++++++++++++++++++++----------------- test/test.js | 64 ++++++++ 3 files changed, 335 insertions(+), 148 deletions(-) diff --git a/readme.md b/readme.md index 73bfa27db..6b2146d73 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ # UAParser.js -JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data with relatively small footprint (~17KB minified, ~6KB gzipped) that can be used either in browser (client-side) or node.js (server-side). +JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data that can be used either in browser (client-side) or node.js (server-side). * Author : Faisal Salman <> * Demo : https://faisalman.github.io/ua-parser-js @@ -26,24 +26,20 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro -51degrees +51degrees ↗ @51degrees/ua-parser-js

UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.

This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).

Visit ↗ 51Degrees UAParser to get started.

- - -

On 6 March, we’ll be hosting a demonstration of the 51Degrees UAParser. Register your place for the webinar ↗ here.

- --- # Documentation -### UAParser([user-agent:string][,extensions:object]) +### UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)]) In The Browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in nodejs for the function to work. Usually you can find the user agent in: @@ -55,7 +51,7 @@ When you call `UAParser` with the `new` keyword `UAParser` will return a new ins Like so: * `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])` ```js -let parser = new UAParser("user-agent"); // you need to pass the user-agent for nodejs +let parser = new UAParser("your user-agent here"); // you need to pass the user-agent for nodejs console.log(parser); // {} let parserResults = parser.getResult(); console.log(parserResults); @@ -297,24 +293,30 @@ If you want to detect something that's not currently provided by UAParser.js (eg ```js // Example: -let myOwnListOfBrowsers = [ +const myOwnListOfBrowsers = [ [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] ]; + +const myUA = 'Mozilla/5.0 MyBrowser/1.3'; + let myParser = new UAParser({ browser: myOwnListOfBrowsers }); -let myUA = 'Mozilla/5.0 MyBrowser/1.3'; + console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3", major: "1", type : "bot"} console.log(myParser.getBrowser().is('bot')); // true // Another example: -let myOwnListOfDevices = [ +const myOwnListOfDevices = [ [/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]], [/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]] ]; + +const myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; + let myParser2 = new UAParser({ browser: myOwnListOfBrowsers, device: myOwnListOfDevices }); -let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; + console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"} ``` @@ -408,6 +410,17 @@ var uap = require('ua-parser-js'); http.createServer(function (req, res) { // get user-agent header var ua = uap(req.headers['user-agent']); + + /* // BEGIN since@1.1 - you can also pass client-hints data to UAParser + + var getHighEntropyValues = 'Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness'; + res.setHeader('Accept-CH', getHighEntropyValues); + res.setHeader('Critical-CH', getHighEntropyValues); + + var ua = uap(req.headers); + + // END since@1.1 */ + // write the result as response res.end(JSON.stringify(ua, null, ' ')); }) diff --git a/src/ua-parser.js b/src/ua-parser.js index 7a3dc7a5e..f9b4edce1 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -38,7 +38,15 @@ WEARABLE = 'wearable', EMBEDDED = 'embedded', USER_AGENT = 'user-agent', - UA_MAX_LENGTH = 350; + UA_MAX_LENGTH = 350, + CH_HEADER = 'sec-ch-ua', + CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', + CH_HEADER_ARCH = CH_HEADER + '-arch', + CH_HEADER_BITNESS = CH_HEADER + '-bitness', + CH_HEADER_MOBILE = CH_HEADER + '-mobile', + CH_HEADER_MODEL = CH_HEADER + '-model', + CH_HEADER_PLATFORM = CH_HEADER + '-platform', + CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version'; var AMAZON = 'Amazon', APPLE = 'Apple', @@ -63,7 +71,8 @@ ZTE = 'ZTE', FACEBOOK = 'Facebook', CHROMIUM_OS = 'Chromium OS', - MAC_OS = 'Mac OS'; + MAC_OS = 'Mac OS', + WINDOWS = 'Windows'; /////////// // Helper @@ -110,6 +119,8 @@ var rgxMapper = function (ua, arrays) { + if(!ua || !arrays) return; + var i = 0, j, k, p, q, matches, match; // loop through all regexes maps @@ -718,7 +729,7 @@ /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ /(win(?=3|9|n)|win 9x )([nt\d\.]+)/i - ], [[NAME, 'Windows'], [VERSION, strMapper, windowsVersionMap]], [ + ], [[NAME, WINDOWS], [VERSION, strMapper, windowsVersionMap]], [ // iOS/macOS /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS @@ -788,115 +799,174 @@ // Constructor //////////////// - function UAItem () {} + function UAItem (data) { + if (!data) return; + this.ua = data[0]; + this.rgxMap = data[1]; + this.data = (function (data) { + var is_ignoreProps = data[3], + is_ignoreRgx = data[4], + toString_props = data[5]; + + var UAData = function () { + for (var init_props in data[2]) { + this[data[2][init_props]] = undefined; + } + }; + UAData.prototype.is = function (strToCheck) { + var is = false; + for (var i in this) { + if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; + } + } + return is; + }; + UAData.prototype.toString = function () { + var str = EMPTY; + for (var i in toString_props) { + if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[toString_props[i]]; + } + } + return str ? str : UNDEF_TYPE; + }; + return new UAData(); + })(data); + } UAItem.prototype.get = function (prop) { - if (!prop) { - return this.data; - } + if (!prop) return this.data; return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; }; - UAItem.prototype.parse = function (ua, rgxmap) { - rgxMapper.call(this.data, ua, rgxmap); + UAItem.prototype.parse = function () { + rgxMapper.call(this.data, this.ua, this.rgxMap); + return this; }; UAItem.prototype.set = function (prop, val) { this.data[prop] = val; - }; - UAItem.prototype.then = function (callback) { - return callback(this.data); - }; - UAItem.createUAData = function (data) { - var is_ignoreProps = data.is_ignoreProps, - is_ignoreRgx = data.is_ignoreRgx, - toString_props = data.toString_props; - - var UAData = function () { - for (var i in data.init_props) { - this[data.init_props[i]] = undefined; - } - }; - UAData.prototype.is = function (strToCheck) { - var is = false; - for (var i in this) { - if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { - is = true; - if (strToCheck != UNDEF_TYPE) break; - } else if (strToCheck == UNDEF_TYPE && is) { - is = !is; - break; - } - } - return is; - }; - UAData.prototype.toString = function () { - var str = EMPTY; - for (var i in toString_props) { - if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { - str += (str ? ' ' : EMPTY) + this[toString_props[i]]; - } - } - return str ? str : UNDEF_TYPE; - }; - return new UAData(); + return this; }; - function UABrowser () { - this.data = UAItem.createUAData({ - init_props : [NAME, VERSION, MAJOR], - is_ignoreProps : [VERSION, MAJOR], - is_ignoreRgx : ' ?browser$', - toString_props : [NAME, VERSION] - }); + function UABrowser (ua, browserMap) { + UAItem.call(this, [ + ua, + browserMap, + [NAME, VERSION, MAJOR], + [VERSION, MAJOR], + ' ?browser$', + [NAME, VERSION] + ]); } UABrowser.prototype = new UAItem(); + UABrowser.prototype.parse = function (uach) { + if (uach) { + var brands = uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER]; + if (brands) { + brands = brands.replace(/\\?\"/g, EMPTY).split(', '); + for (var i in brands) { + var brand = brands[i].split(';v='), + brandName = brand[0], + brandVersion = brand[1]; + if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) { + this.set(NAME, brandName.replace(GOOGLE+' ', EMPTY)) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + } + } + } + if (!this.get(NAME)) UAItem.prototype.parse.call(this); + return this; + }; - function UACPU () { - this.data = UAItem.createUAData({ - init_props : [ARCHITECTURE], - is_ignoreProps : [], - toString_props : [ARCHITECTURE] - }); + function UACPU (ua, cpuMap) { + UAItem.call(this, [ + ua, + cpuMap, + [ARCHITECTURE], + [], + null, + [ARCHITECTURE] + ]); } UACPU.prototype = new UAItem(); + UACPU.prototype.parse = function (uach) { + if (uach) { + var archName = uach[CH_HEADER_ARCH]; + archName += (archName && uach[CH_HEADER_BITNESS] == '64') ? '64' : EMPTY; + rgxMapper.call(this.data, archName, this.rgxMap); + } + if (!this.get(ARCHITECTURE)) UAItem.prototype.parse.call(this); + return this; + }; - function UADevice () { - this.data = UAItem.createUAData({ - init_props : [TYPE, MODEL, VENDOR], - is_ignoreProps : [], - toString_props : [VENDOR, MODEL] - }); + function UADevice (ua, deviceMap) { + UAItem.call(this, [ + ua, + deviceMap, + [TYPE, MODEL, VENDOR], + [], + null, + [VENDOR, MODEL] + ]); } UADevice.prototype = new UAItem(); + UADevice.prototype.parse = function (uach) { + if (uach) { + this.set(TYPE, uach[CH_HEADER_MOBILE] == '?1' ? + MOBILE : + this.get(TYPE)) + .set(MODEL, uach[CH_HEADER_MODEL] ? + uach[CH_HEADER_MODEL].replace(/\"/g, EMPTY) : + this.get(MODEL)); + } + if (!this.get(TYPE) && !this.get(MODEL)) UAItem.prototype.parse.call(this); + return this; + }; - function UAEngine () { - this.data = UAItem.createUAData({ - init_props : [NAME, VERSION], - is_ignoreProps : [VERSION], - toString_props : [NAME, VERSION] - }); + function UAEngine (ua, engineMap) { + UAItem.call(this, [ + ua, + engineMap, + [NAME, VERSION], + [VERSION], + null, + [NAME, VERSION] + ]); } UAEngine.prototype = new UAItem(); - function UAOS () { - this.data = UAItem.createUAData({ - init_props : [NAME, VERSION], - is_ignoreProps : [VERSION], - is_ignoreRgx : ' ?os$', - toString_props : [NAME, VERSION] - }); + function UAOS (ua, osMap) { + UAItem.call(this, [ + ua, + osMap, + [NAME, VERSION], + [VERSION], + ' ?os$', + [NAME, VERSION] + ]); } UAOS.prototype = new UAItem(); - - function UAResult () { - this.data = { - ua : '', - browser : undefined, - cpu : undefined, - device : undefined, - engine : undefined, - os : undefined - }; - } - UAResult.prototype = new UAItem(); + UAOS.prototype.parse = function (uach) { + if (uach) { + var osName = uach[CH_HEADER_PLATFORM] ? uach[CH_HEADER_PLATFORM].replace(/\"/g, EMPTY) : undefined; + var osVersion = uach[CH_HEADER_PLATFORM_VER] ? uach[CH_HEADER_PLATFORM_VER].replace(/\"/g, EMPTY) : undefined; + osVersion = (osName == WINDOWS) ? (majorize(osVersion) >= 13 ? '11' : '10') : osVersion; + this.set(NAME, osName) + .set(VERSION, osVersion); + } + if (!this.get(NAME)) UAItem.prototype.parse.call(this); + if (/(chrome |mac)os/.test(this.get(NAME))) { + this.set(NAME, this.get(NAME) + .replace(/chrome os/i, CHROMIUM_OS) + .replace(/macos/i, MAC_OS)); + } + return this; + }; function UAParser (ua, extensions, headers) { @@ -908,6 +978,7 @@ extensions = ua; // case UAParser(extensions) } else { headers = ua; // case UAParser(headers) + extensions = undefined; } ua = undefined; } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { @@ -918,75 +989,114 @@ if (!(this instanceof UAParser)) { return new UAParser(ua, extensions, headers).getResult(); } - var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined; - // _ua = user-supplied string || window.navigator.userAgent || user-agent header || empty - var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : (!ua && headers && headers[USER_AGENT] ? headers[USER_AGENT] : EMPTY)); - var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined; - var _rgxmap = extensions ? extend(regexes, extensions) : regexes; + var navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? + window.navigator : + undefined, + + userAgent = ua || + ((navigator && navigator.userAgent) ? + navigator.userAgent : + (headers && headers[USER_AGENT] ? + headers[USER_AGENT] : + EMPTY)), + + clientHints = (navigator && navigator.userAgentData) ? + navigator.userAgentData : + undefined, + + regexMap = extensions ? + extend(regexes, extensions) : + regexes; // public methods this.getBrowser = function () { - var _browser = new UABrowser(); - _browser.parse(_ua, _rgxmap.browser); - _browser.set(MAJOR, majorize(_browser.get(VERSION))); - // Brave-specific detection - if (_navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) { - _browser.set(NAME, 'Brave'); + var browser = new UABrowser(userAgent, regexMap.browser); + if (headers && (headers[CH_HEADER_FULL_VER_LIST] || headers[CH_HEADER])) { + browser.parse(headers); + } else { + browser.parse(); + // Brave-specific detection + if (navigator && navigator.brave && typeof navigator.brave.isBrave == FUNC_TYPE) { + browser.set(NAME, 'Brave'); + } } - return _browser.get(); + return browser + .set(MAJOR, majorize(browser.get(VERSION))) + .get(); }; + this.getCPU = function () { - var _cpu = new UACPU(); - _cpu.parse(_ua, _rgxmap.cpu); - return _cpu.get(); + var cpu = new UACPU(userAgent, regexMap.cpu); + if (headers && headers[CH_HEADER_ARCH]) { + cpu.parse(headers); + } else { + cpu.parse(); + } + return cpu.get(); }; + this.getDevice = function () { - var _device = new UADevice(); - _device.parse(_ua, _rgxmap.device); - if (!_device.get(TYPE) && _uach && _uach.mobile) { - _device.set(TYPE, MOBILE); - } - // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (_device.get(NAME) == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) { - _device.set(MODEL, 'iPad'); - _device.set(TYPE, TABLET); + var device = new UADevice(userAgent, regexMap.device); + if (headers && (headers[CH_HEADER_MOBILE] || headers[CH_HEADER_MODEL])) { + device.parse(headers); + } else { + device.parse(); + if (!device.get(TYPE) && clientHints && clientHints.mobile) { + device.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (device.get(NAME) == 'Macintosh' && navigator && typeof navigator.standalone !== UNDEF_TYPE && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) { + device + .set(MODEL, 'iPad') + .set(TYPE, TABLET); + } } - return _device.get(); + return device.get(); }; + this.getEngine = function () { - var _engine = new UAEngine(); - _engine.parse(_ua, _rgxmap.engine); - return _engine.get(); + return new UAEngine(userAgent, regexMap.engine) + .parse() + .get(); }; + this.getOS = function () { - var _os = new UAOS(); - _os.parse(_ua, _rgxmap.os); - if (!_os.get(NAME) && _uach && _uach.platform != 'Unknown') { - _os.set(NAME, _uach.platform - .replace(/chrome os/i, CHROMIUM_OS) - .replace(/macos/i, MAC_OS)); // backward compatibility + var os = new UAOS(userAgent, regexMap.os); + if (headers && headers[CH_HEADER_PLATFORM]) { + os.parse(headers); + } else { + os.parse(); + if (!os.get(NAME) && clientHints && clientHints.platform != 'Unknown') { + os.set(NAME, clientHints.platform + .replace(/chrome os/i, CHROMIUM_OS) + .replace(/macos/i, MAC_OS)); // backward compatibility + } } - return _os.get(); + return os.get(); }; + this.getResult = function () { - var _result = new UAResult(); - _result.set('ua', _ua); - _result.set('browser', this.getBrowser()); - _result.set('cpu', this.getCPU()); - _result.set('device', this.getDevice()); - _result.set('engine', this.getEngine()); - _result.set('os', this.getOS()); - return _result.get(); + return { + 'ua' : userAgent, + 'browser' : this.getBrowser(), + 'cpu' : this.getCPU(), + 'device' : this.getDevice(), + 'engine' : this.getEngine(), + 'os' : this.getOS() + }; }; + this.getUA = function () { - return _ua; + return userAgent; }; + this.setUA = function (ua) { - _ua = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; + userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; return this; }; - this.setUA(_ua); + + this.setUA(userAgent); return this; } diff --git a/test/test.js b/test/test.js index 339f3c309..88c252296 100644 --- a/test/test.js +++ b/test/test.js @@ -335,4 +335,68 @@ describe('Read user-agent data from req.headers', function () { let engine = UAParser(req.headers).engine; assert.strictEqual(engine.name, "EdgeHTML"); }); +}); + +describe('Map UA-CH headers', function () { + + const headers = { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : 'ARM', + 'sec-ch-ua-bitness' : '64', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : 'Pixel 99', + 'sec-ch-ua-platform' : 'Windows', + 'sec-ch-ua-platform-version' : '13', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; + + const headers2 = { + 'sec-ch-ua-mobile' : '?1', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; + + let uap = UAParser(headers); + let browser = uap.browser; + let cpu = uap.cpu; + let device = uap.device; + let engine = uap.engine; + let os = uap.os; + + it('Can read from client-hints headers', function () { + + assert.strictEqual(browser.name, "Chrome"); + assert.strictEqual(browser.version, "93.0.1.2"); + assert.strictEqual(browser.major, "93"); + assert.strictEqual(cpu.architecture, "arm64"); + assert.strictEqual(device.type, "mobile"); + assert.strictEqual(device.model, "Pixel 99"); + assert.strictEqual(device.vendor, undefined); + assert.strictEqual(engine.name, 'Blink'); + assert.strictEqual(engine.version, '110.0.0.0'); + assert.strictEqual(os.name, "Windows"); + assert.strictEqual(os.version, "11"); + }); + + it('Can read from user-agent header', function () { + + uap = UAParser(headers2); + browser = uap.browser; + cpu = uap.cpu; + device = uap.device; + engine = uap.engine; + os = uap.os; + + assert.strictEqual(browser.name, "Chrome"); + assert.strictEqual(browser.version, "110.0.0.0"); + assert.strictEqual(browser.major, "110"); + assert.strictEqual(cpu.architecture, "amd64"); + assert.strictEqual(device.type, "mobile"); + assert.strictEqual(device.model, undefined); + assert.strictEqual(device.vendor, undefined); + assert.strictEqual(engine.name, 'Blink'); + assert.strictEqual(engine.version, '110.0.0.0'); + assert.strictEqual(os.name, "Linux"); + assert.strictEqual(os.version, "x86_64"); + }); }); \ No newline at end of file From 5672a2e15c30a0b0f9181b0b2c0c57e75c1a9068 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 9 Mar 2023 23:51:23 +0700 Subject: [PATCH 036/388] Expose UA-CH data in getResult() --- src/ua-parser.js | 185 ++++++++++++++++++++++++++++------------------- test/test.js | 1 + 2 files changed, 110 insertions(+), 76 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index f9b4edce1..d0a9d2be5 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -95,20 +95,34 @@ has = function (str1, str2) { return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, + initialize = function (arr) { + for (var i in arr) { + var propName = arr[i]; + if (typeof propName == OBJ_TYPE && propName.length == 2) { + this[propName[0]] = propName[1]; + } else { + this[propName] = undefined; + } + } + return this; + }, isExtensions = function (obj) { for (var prop in obj) { return /^(browser|cpu|device|engine|os)$/.test(prop); } }, lowerize = function (str, rgx) { - return typeof(str) === STR_TYPE ? str.toLowerCase().replace((rgx ? new RegExp(rgx, 'i') : EMPTY), EMPTY) : str; + return typeof(str) === STR_TYPE ? strip((rgx ? new RegExp(rgx, 'i') : EMPTY), str.toLowerCase()) : str; }, majorize = function (version) { - return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] : undefined; + return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; + }, + strip = function (pattern, str) { + return str.replace(pattern, EMPTY); }, trim = function (str, len) { if (typeof(str) === STR_TYPE) { - str = str.replace(/^\s\s*/, EMPTY); + str = strip(/^\s\s*/, str); return typeof(len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); } }; @@ -799,20 +813,45 @@ // Constructor //////////////// + function UACHData (uach, isBrowser) { + uach = uach || {}; + if (!isBrowser) { + initialize.call(this, ['brands', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness']); + if ((uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])) { + this.brands = []; + var tokens = strip(/\\?\"/g, (uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])).split(', '); + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i].split(';v='); + this.brands[i] = { brand : token[0], version : token[1] }; + } + } + this.mobile = uach[CH_HEADER_MOBILE] == '?1'; + var setHeader = function (header) { + return header ? strip(/\"/g, header) : undefined; + }; + this.model = setHeader(uach[CH_HEADER_MODEL]); + this.platform = setHeader(uach[CH_HEADER_PLATFORM]); + this.platformVersion = setHeader(uach[CH_HEADER_PLATFORM_VER]); + this.architecture = setHeader(uach[CH_HEADER_ARCH]); + this.bitness = setHeader(uach[CH_HEADER_BITNESS]); + } + return this; + } + function UAItem (data) { if (!data) return; this.ua = data[0]; this.rgxMap = data[1]; + this.uaCH = data[2]; this.data = (function (data) { - var is_ignoreProps = data[3], - is_ignoreRgx = data[4], - toString_props = data[5]; + var init_props = data[3], + is_ignoreProps = data[4], + is_ignoreRgx = data[5], + toString_props = data[6]; - var UAData = function () { - for (var init_props in data[2]) { - this[data[2][init_props]] = undefined; - } - }; + function UAData () { + initialize.call(this, init_props); + } UAData.prototype.is = function (strToCheck) { var is = false; for (var i in this) { @@ -851,10 +890,11 @@ return this; }; - function UABrowser (ua, browserMap) { + function UABrowser (ua, browserMap, uach) { UAItem.call(this, [ ua, browserMap, + uach, [NAME, VERSION, MAJOR], [VERSION, MAJOR], ' ?browser$', @@ -862,31 +902,27 @@ ]); } UABrowser.prototype = new UAItem(); - UABrowser.prototype.parse = function (uach) { - if (uach) { - var brands = uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER]; - if (brands) { - brands = brands.replace(/\\?\"/g, EMPTY).split(', '); - for (var i in brands) { - var brand = brands[i].split(';v='), - brandName = brand[0], - brandVersion = brand[1]; - if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) { - this.set(NAME, brandName.replace(GOOGLE+' ', EMPTY)) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - } + UABrowser.prototype.parseCH = function () { + var brands = this.uaCH.brands; + if (brands) { + for (var i in brands) { + var brandName = brands[i].brand, + brandVersion = brands[i].version; + if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) { + this.set(NAME, strip(GOOGLE+' ', brandName)) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); } } } - if (!this.get(NAME)) UAItem.prototype.parse.call(this); return this; }; - function UACPU (ua, cpuMap) { + function UACPU (ua, cpuMap, uach) { UAItem.call(this, [ ua, cpuMap, + uach, [ARCHITECTURE], [], null, @@ -894,20 +930,18 @@ ]); } UACPU.prototype = new UAItem(); - UACPU.prototype.parse = function (uach) { - if (uach) { - var archName = uach[CH_HEADER_ARCH]; - archName += (archName && uach[CH_HEADER_BITNESS] == '64') ? '64' : EMPTY; - rgxMapper.call(this.data, archName, this.rgxMap); - } - if (!this.get(ARCHITECTURE)) UAItem.prototype.parse.call(this); + UACPU.prototype.parseCH = function () { + var archName = this.uaCH.architecture; + archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY; + rgxMapper.call(this.data, archName, this.rgxMap); return this; }; - function UADevice (ua, deviceMap) { + function UADevice (ua, deviceMap, uach) { UAItem.call(this, [ ua, deviceMap, + uach, [TYPE, MODEL, VENDOR], [], null, @@ -915,16 +949,13 @@ ]); } UADevice.prototype = new UAItem(); - UADevice.prototype.parse = function (uach) { - if (uach) { - this.set(TYPE, uach[CH_HEADER_MOBILE] == '?1' ? - MOBILE : - this.get(TYPE)) - .set(MODEL, uach[CH_HEADER_MODEL] ? - uach[CH_HEADER_MODEL].replace(/\"/g, EMPTY) : - this.get(MODEL)); + UADevice.prototype.parseCH = function () { + if (this.uaCH.mobile) { + this.set(TYPE, MOBILE); + } + if (this.uaCH.model) { + this.set(MODEL, strip(/\"/g, this.uaCH.model)); } - if (!this.get(TYPE) && !this.get(MODEL)) UAItem.prototype.parse.call(this); return this; }; @@ -932,6 +963,7 @@ UAItem.call(this, [ ua, engineMap, + null, [NAME, VERSION], [VERSION], null, @@ -940,10 +972,11 @@ } UAEngine.prototype = new UAItem(); - function UAOS (ua, osMap) { + function UAOS (ua, osMap, uach) { UAItem.call(this, [ ua, osMap, + uach, [NAME, VERSION], [VERSION], ' ?os$', @@ -951,22 +984,15 @@ ]); } UAOS.prototype = new UAItem(); - UAOS.prototype.parse = function (uach) { - if (uach) { - var osName = uach[CH_HEADER_PLATFORM] ? uach[CH_HEADER_PLATFORM].replace(/\"/g, EMPTY) : undefined; - var osVersion = uach[CH_HEADER_PLATFORM_VER] ? uach[CH_HEADER_PLATFORM_VER].replace(/\"/g, EMPTY) : undefined; - osVersion = (osName == WINDOWS) ? (majorize(osVersion) >= 13 ? '11' : '10') : osVersion; - this.set(NAME, osName) - .set(VERSION, osVersion); - } - if (!this.get(NAME)) UAItem.prototype.parse.call(this); - if (/(chrome |mac)os/.test(this.get(NAME))) { - this.set(NAME, this.get(NAME) - .replace(/chrome os/i, CHROMIUM_OS) - .replace(/macos/i, MAC_OS)); - } + UAOS.prototype.parseCH = function (uach) { + var osName = this.uaCH.platform; + var osVersion = this.uaCH.platformVersion; + osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; + this.set(NAME, osName) + .set(VERSION, osVersion); return this; }; + function UAParser (ua, extensions, headers) { @@ -1000,8 +1026,10 @@ (headers && headers[USER_AGENT] ? headers[USER_AGENT] : EMPTY)), + + clientHints = new UACHData(headers, false), - clientHints = (navigator && navigator.userAgentData) ? + userAgentData = (navigator && navigator.userAgentData) ? navigator.userAgentData : undefined, @@ -1011,10 +1039,11 @@ // public methods this.getBrowser = function () { - var browser = new UABrowser(userAgent, regexMap.browser); + var browser = new UABrowser(userAgent, regexMap.browser, clientHints); if (headers && (headers[CH_HEADER_FULL_VER_LIST] || headers[CH_HEADER])) { - browser.parse(headers); - } else { + browser.parseCH(); + } + if (!browser.get(NAME)) { browser.parse(); // Brave-specific detection if (navigator && navigator.brave && typeof navigator.brave.isBrave == FUNC_TYPE) { @@ -1027,22 +1056,24 @@ }; this.getCPU = function () { - var cpu = new UACPU(userAgent, regexMap.cpu); + var cpu = new UACPU(userAgent, regexMap.cpu, clientHints); if (headers && headers[CH_HEADER_ARCH]) { - cpu.parse(headers); - } else { + cpu.parseCH(); + } + if (!cpu.get(ARCHITECTURE)) { cpu.parse(); } return cpu.get(); }; this.getDevice = function () { - var device = new UADevice(userAgent, regexMap.device); + var device = new UADevice(userAgent, regexMap.device, clientHints); if (headers && (headers[CH_HEADER_MOBILE] || headers[CH_HEADER_MODEL])) { - device.parse(headers); - } else { + device.parseCH(); + } + if (!device.get(TYPE) || !device.get(MODEL)) { device.parse(); - if (!device.get(TYPE) && clientHints && clientHints.mobile) { + if (!device.get(TYPE) && userAgentData && userAgentData.mobile) { device.set(TYPE, MOBILE); } // iPadOS-specific detection: identified as Mac, but has some iOS-only properties @@ -1062,13 +1093,14 @@ }; this.getOS = function () { - var os = new UAOS(userAgent, regexMap.os); + var os = new UAOS(userAgent, regexMap.os, clientHints); if (headers && headers[CH_HEADER_PLATFORM]) { - os.parse(headers); - } else { + os.parseCH(); + } + if (!os.get(NAME)) { os.parse(); - if (!os.get(NAME) && clientHints && clientHints.platform != 'Unknown') { - os.set(NAME, clientHints.platform + if (!os.get(NAME) && userAgentData && userAgentData.platform != 'Unknown') { + os.set(NAME, userAgentData.platform .replace(/chrome os/i, CHROMIUM_OS) .replace(/macos/i, MAC_OS)); // backward compatibility } @@ -1079,6 +1111,7 @@ this.getResult = function () { return { 'ua' : userAgent, + 'ua_ch' : clientHints, 'browser' : this.getBrowser(), 'cpu' : this.getCPU(), 'device' : this.getDevice(), diff --git a/test/test.js b/test/test.js index 88c252296..677e02b90 100644 --- a/test/test.js +++ b/test/test.js @@ -82,6 +82,7 @@ describe('Returns', function () { assert.deepEqual(new UAParser('').getResult(), { ua : '', + ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, browser: { name: undefined, version: undefined, major: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, From aff5a209f8840b4465e6a698acebddcfa02b5005 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 11 Mar 2023 02:08:15 +0700 Subject: [PATCH 037/388] Add new method: withClientHints() #408 #566 #588 --- readme.md | 65 ++++++++-- src/ua-parser.js | 312 +++++++++++++++++++++++++++-------------------- test/test.js | 10 +- 3 files changed, 240 insertions(+), 147 deletions(-) diff --git a/readme.md b/readme.md index 6b2146d73..c37673527 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ # UAParser.js -JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data that can be used either in browser (client-side) or node.js (server-side). +JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client-Hints data that can be used either in browser (client-side) or node.js (server-side). * Author : Faisal Salman <> * Demo : https://faisalman.github.io/ua-parser-js @@ -56,25 +56,28 @@ console.log(parser); // {} let parserResults = parser.getResult(); console.log(parserResults); /** { - "ua": "", - "browser": {}, - "engine": {}, - "os": {}, - "device": {}, - "cpu": {} + "ua" : "", + "browser" : {}, + "engine" : {}, + "os" : {}, + "device" : {}, + "cpu" : {} + + // since@1.1 + ,"ua_ch" : {} } */ ``` When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results. * `UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])` - * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` + * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }` ## Methods #### Methods table The methods are self explanatory, here's a small overview on all the available methods: * `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os: -`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`. +`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`. * `getBrowser()` - returns the browser name and version. * `getDevice()` - returns the device model, type, vendor. @@ -87,7 +90,7 @@ The methods are self explanatory, here's a small overview on all the available m --- * `getResult()` - * returns `{ ua: '', browser: UABrowser {}, cpu: UACPU {}, device: UADevice {}, engine: UAEngine {}, os: UAOS {} }` + * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }` * `getBrowser()` * returns `{ name: '', version: '' }` @@ -180,7 +183,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal * set UA string to be parsed * returns current instance -#### * is() utility `since@1.1` +#### * `is()` utility `since@1.1` ```js // Is just a shorthand to check whether specified item has a property with equals value (case-insensitive) @@ -244,7 +247,7 @@ let engine = uap.getEngine(); engine.is("Blink"); // true ``` -#### * toString() utility `since@1.1` +#### * `toString()` utility `since@1.1` ```js // Retrieve full-name values as a string @@ -285,9 +288,33 @@ engine.version; // "28.0.1500.95" engine.toString(); // "Blink 28.0.1500.95" ``` +#### * `withClientHints()` `since@1.1` + +Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`. + +```js +(async function () { + let ua = new UAParser(); + + // get browser data from user-agent only : + let browser = ua.getBrowser(); + console.log('Using User-Agent: ', browser); + + // get browser data from client-hints (with user-agent as fallback) : + browser = await ua.getBrowser().withClientHints(); + console.log('Using Client-Hints: ', browser); + + // alternatively : + ua.getBrowser().withClientHints().then(function (browser) { + console.log('Using Client-Hints: ', browser); + }); +})(); +``` + + ## Extending Regex -If you want to detect something that's not currently provided by UAParser.js (eg: bots, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own. +If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own. * `UAParser([uastring,] extensions [,headers:object(since@1.1)])` @@ -359,6 +386,18 @@ console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "1 cpu: { architecture: "" } + + // added since@1.1: + ,ua_ch: { + architecture: "", + brands: "", + bitness: "", + fullVersionList: "", + mobile: "", + model: "", + platform: "", + platformVersion: "" + } } */ // Default result depends on current window.navigator.userAgent value diff --git a/src/ua-parser.js b/src/ua-parser.js index d0a9d2be5..0695044d2 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -46,7 +46,8 @@ CH_HEADER_MOBILE = CH_HEADER + '-mobile', CH_HEADER_MODEL = CH_HEADER + '-model', CH_HEADER_PLATFORM = CH_HEADER + '-platform', - CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version'; + CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', + CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness']; var AMAZON = 'Amazon', APPLE = 'Apple', @@ -73,6 +74,13 @@ CHROMIUM_OS = 'Chromium OS', MAC_OS = 'Mac OS', WINDOWS = 'Windows'; + + var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ? + window.navigator : + undefined, + NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ? + NAVIGATOR.userAgentData : + undefined; /////////// // Helper @@ -813,19 +821,23 @@ // Constructor //////////////// - function UACHData (uach, isBrowser) { + function UAParserDataCH (uach, isHTTP_UACH) { uach = uach || {}; - if (!isBrowser) { - initialize.call(this, ['brands', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness']); - if ((uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])) { - this.brands = []; - var tokens = strip(/\\?\"/g, (uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])).split(', '); + initialize.call(this, CH_ALL_VALUES); + if (isHTTP_UACH) { + var toArray = function (header) { + if (!header) return undefined; + var arr = []; + var tokens = strip(/\\?\"/g, header).split(', '); for (var i = 0; i < tokens.length; i++) { var token = tokens[i].split(';v='); - this.brands[i] = { brand : token[0], version : token[1] }; + arr[i] = { brand : token[0], version : token[1] }; } - } - this.mobile = uach[CH_HEADER_MOBILE] == '?1'; + return arr; + }; + this.brands = toArray(uach[CH_HEADER]); + this.fullVersionList = toArray(uach[CH_HEADER_FULL_VER_LIST]); + this.mobile = /\?1/.test(uach[CH_HEADER_MOBILE]); var setHeader = function (header) { return header ? strip(/\"/g, header) : undefined; }; @@ -834,25 +846,32 @@ this.platformVersion = setHeader(uach[CH_HEADER_PLATFORM_VER]); this.architecture = setHeader(uach[CH_HEADER_ARCH]); this.bitness = setHeader(uach[CH_HEADER_BITNESS]); + } else { + for (var prop in uach) { + this[prop] = uach[prop]; + } } return this; } - function UAItem (data) { + function UAParserItem (data) { if (!data) return; this.ua = data[0]; - this.rgxMap = data[1]; - this.uaCH = data[2]; + this.uaCH = data[1]; + this.rgxMap = data[3]; this.data = (function (data) { - var init_props = data[3], - is_ignoreProps = data[4], - is_ignoreRgx = data[5], - toString_props = data[6]; - - function UAData () { + var ua = data[0], + itemType = data[2], + rgxMap = data[3], + init_props = data[4], + is_ignoreProps = data[5], + is_ignoreRgx = data[6], + toString_props = data[7]; + + function UAParserData () { initialize.call(this, init_props); } - UAData.prototype.is = function (strToCheck) { + UAParserData.prototype.is = function (strToCheck) { var is = false; for (var i in this) { if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { @@ -865,7 +884,7 @@ } return is; }; - UAData.prototype.toString = function () { + UAParserData.prototype.toString = function () { var str = EMPTY; for (var i in toString_props) { if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { @@ -874,36 +893,83 @@ } return str ? str : UNDEF_TYPE; }; - return new UAData(); + UAParserData.prototype.withClientHints = function () { + if (!NAVIGATOR_UADATA) return; + return NAVIGATOR_UADATA + .getHighEntropyValues(CH_ALL_VALUES) + .then(function (res) { + + var JS_UACH = new UAParserDataCH(res, false), + browser = new UAParserBrowser(ua, rgxMap, JS_UACH).get(), + cpu = new UAParserCPU(ua, rgxMap, JS_UACH).get(), + device = new UAParserDevice(ua, rgxMap, JS_UACH).get(), + engine = new UAParserEngine(ua, rgxMap).get(), + os = new UAParserOS(ua, rgxMap, JS_UACH).get(); + + switch (itemType) { + case 'browser': + return browser; + case 'cpu': + return cpu; + case 'device': + return device; + case 'engine': + return engine; + case 'os': + return os; + default : + return { + 'ua' : ua, + 'ua_ch' : JS_UACH, + 'browser' : browser, + 'cpu' : cpu, + 'device' : device, + 'engine' : engine, + 'os' : os + }; + } + }); + }; + return new UAParserData(); })(data); } - UAItem.prototype.get = function (prop) { + UAParserItem.prototype.get = function (prop) { if (!prop) return this.data; return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; }; - UAItem.prototype.parse = function () { + UAParserItem.prototype.parse = function () { rgxMapper.call(this.data, this.ua, this.rgxMap); return this; }; - UAItem.prototype.set = function (prop, val) { + UAParserItem.prototype.set = function (prop, val) { this.data[prop] = val; return this; }; - function UABrowser (ua, browserMap, uach) { - UAItem.call(this, [ + function UAParserBrowser (ua, browserMap, uach) { + UAParserItem.call(this, [ ua, - browserMap, uach, + 'browser', + browserMap, [NAME, VERSION, MAJOR], [VERSION, MAJOR], ' ?browser$', [NAME, VERSION] ]); + this.parseCH(); + if (!this.get(NAME)) { + this.parse(); + // Brave-specific detection + if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); + } + } + this.set(MAJOR, majorize(this.get(VERSION))); } - UABrowser.prototype = new UAItem(); - UABrowser.prototype.parseCH = function () { - var brands = this.uaCH.brands; + UAParserBrowser.prototype = new UAParserItem(); + UAParserBrowser.prototype.parseCH = function () { + var brands = this.uaCH.fullVersionList || this.uaCH.brands; if (brands) { for (var i in brands) { var brandName = brands[i].brand, @@ -918,38 +984,58 @@ return this; }; - function UACPU (ua, cpuMap, uach) { - UAItem.call(this, [ + function UAParserCPU (ua, cpuMap, uach) { + UAParserItem.call(this, [ ua, - cpuMap, uach, + 'cpu', + cpuMap, [ARCHITECTURE], [], null, [ARCHITECTURE] ]); + this.parseCH(); + if (!this.get(ARCHITECTURE)) { + this.parse(); + } } - UACPU.prototype = new UAItem(); - UACPU.prototype.parseCH = function () { + UAParserCPU.prototype = new UAParserItem(); + UAParserCPU.prototype.parseCH = function () { var archName = this.uaCH.architecture; - archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY; - rgxMapper.call(this.data, archName, this.rgxMap); + if (archName) { + archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY; + rgxMapper.call(this.data, archName, this.rgxMap); + } return this; }; - function UADevice (ua, deviceMap, uach) { - UAItem.call(this, [ + function UAParserDevice (ua, deviceMap, uach) { + UAParserItem.call(this, [ ua, - deviceMap, uach, + 'device', + deviceMap, [TYPE, MODEL, VENDOR], [], null, [VENDOR, MODEL] ]); + this.parseCH(); + if (!this.get(TYPE) || !this.get(MODEL)) { + this.parse(); + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.mobile) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); + } + } } - UADevice.prototype = new UAItem(); - UADevice.prototype.parseCH = function () { + UAParserDevice.prototype = new UAParserItem(); + UAParserDevice.prototype.parseCH = function () { if (this.uaCH.mobile) { this.set(TYPE, MOBILE); } @@ -959,41 +1045,59 @@ return this; }; - function UAEngine (ua, engineMap) { - UAItem.call(this, [ + function UAParserEngine (ua, engineMap) { + UAParserItem.call(this, [ ua, - engineMap, null, + 'engine', + engineMap, [NAME, VERSION], [VERSION], null, [NAME, VERSION] ]); + this.parse(); } - UAEngine.prototype = new UAItem(); + UAParserEngine.prototype = new UAParserItem(); - function UAOS (ua, osMap, uach) { - UAItem.call(this, [ + function UAParserOS (ua, osMap, uach) { + UAParserItem.call(this, [ ua, - osMap, uach, + 'os', + osMap, [NAME, VERSION], [VERSION], ' ?os$', [NAME, VERSION] ]); + this.parseCH(); + if (!this.get(NAME)) { + this.parse(); + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform != 'Unknown') { + this.set(NAME, NAVIGATOR_UADATA.platform + .replace(/chrome os/i, CHROMIUM_OS) + .replace(/macos/i, MAC_OS)); // backward compatibility + } + } } - UAOS.prototype = new UAItem(); - UAOS.prototype.parseCH = function (uach) { + UAParserOS.prototype = new UAParserItem(); + UAParserOS.prototype.parseCH = function () { var osName = this.uaCH.platform; - var osVersion = this.uaCH.platformVersion; - osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; - this.set(NAME, osName) - .set(VERSION, osVersion); + if(osName) { + var osVersion = this.uaCH.platformVersion; + osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; + this.set(NAME, osName) + .set(VERSION, osVersion); + } return this; }; - + function UAParserResult (ua, uach) { + UAParserItem.call(this, [ua, uach]); + } + UAParserResult.prototype = new UAParserItem(); + function UAParser (ua, extensions, headers) { if (typeof ua === OBJ_TYPE) { @@ -1016,22 +1120,14 @@ return new UAParser(ua, extensions, headers).getResult(); } - var navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? - window.navigator : - undefined, - - userAgent = ua || - ((navigator && navigator.userAgent) ? - navigator.userAgent : + var userAgent = ua || + ((NAVIGATOR && NAVIGATOR.userAgent) ? + NAVIGATOR.userAgent : (headers && headers[USER_AGENT] ? headers[USER_AGENT] : EMPTY)), - clientHints = new UACHData(headers, false), - - userAgentData = (navigator && navigator.userAgentData) ? - navigator.userAgentData : - undefined, + HTTP_UACH = new UAParserDataCH(headers, true), regexMap = extensions ? extend(regexes, extensions) : @@ -1039,85 +1135,35 @@ // public methods this.getBrowser = function () { - var browser = new UABrowser(userAgent, regexMap.browser, clientHints); - if (headers && (headers[CH_HEADER_FULL_VER_LIST] || headers[CH_HEADER])) { - browser.parseCH(); - } - if (!browser.get(NAME)) { - browser.parse(); - // Brave-specific detection - if (navigator && navigator.brave && typeof navigator.brave.isBrave == FUNC_TYPE) { - browser.set(NAME, 'Brave'); - } - } - return browser - .set(MAJOR, majorize(browser.get(VERSION))) - .get(); + return new UAParserBrowser(userAgent, regexMap.browser, HTTP_UACH).get(); }; this.getCPU = function () { - var cpu = new UACPU(userAgent, regexMap.cpu, clientHints); - if (headers && headers[CH_HEADER_ARCH]) { - cpu.parseCH(); - } - if (!cpu.get(ARCHITECTURE)) { - cpu.parse(); - } - return cpu.get(); + return new UAParserCPU(userAgent, regexMap.cpu, HTTP_UACH).get(); }; this.getDevice = function () { - var device = new UADevice(userAgent, regexMap.device, clientHints); - if (headers && (headers[CH_HEADER_MOBILE] || headers[CH_HEADER_MODEL])) { - device.parseCH(); - } - if (!device.get(TYPE) || !device.get(MODEL)) { - device.parse(); - if (!device.get(TYPE) && userAgentData && userAgentData.mobile) { - device.set(TYPE, MOBILE); - } - // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (device.get(NAME) == 'Macintosh' && navigator && typeof navigator.standalone !== UNDEF_TYPE && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) { - device - .set(MODEL, 'iPad') - .set(TYPE, TABLET); - } - } - return device.get(); + return new UAParserDevice(userAgent, regexMap.device, HTTP_UACH).get(); }; this.getEngine = function () { - return new UAEngine(userAgent, regexMap.engine) - .parse() - .get(); + return new UAParserEngine(userAgent, regexMap.engine).get(); }; this.getOS = function () { - var os = new UAOS(userAgent, regexMap.os, clientHints); - if (headers && headers[CH_HEADER_PLATFORM]) { - os.parseCH(); - } - if (!os.get(NAME)) { - os.parse(); - if (!os.get(NAME) && userAgentData && userAgentData.platform != 'Unknown') { - os.set(NAME, userAgentData.platform - .replace(/chrome os/i, CHROMIUM_OS) - .replace(/macos/i, MAC_OS)); // backward compatibility - } - } - return os.get(); + return new UAParserOS(userAgent, regexMap.os, HTTP_UACH).get(); }; this.getResult = function () { - return { - 'ua' : userAgent, - 'ua_ch' : clientHints, - 'browser' : this.getBrowser(), - 'cpu' : this.getCPU(), - 'device' : this.getDevice(), - 'engine' : this.getEngine(), - 'os' : this.getOS() - }; + return new UAParserResult(userAgent, HTTP_UACH) + .set('ua', userAgent) + .set('ua_ch', HTTP_UACH) + .set('browser', this.getBrowser()) + .set('cpu', this.getCPU()) + .set('device', this.getDevice()) + .set('engine', this.getEngine()) + .set('os', this.getOS()) + .get(); }; this.getUA = function () { diff --git a/test/test.js b/test/test.js index 677e02b90..30f26d108 100644 --- a/test/test.js +++ b/test/test.js @@ -82,7 +82,7 @@ describe('Returns', function () { assert.deepEqual(new UAParser('').getResult(), { ua : '', - ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, + ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, browser: { name: undefined, version: undefined, major: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, @@ -400,4 +400,12 @@ describe('Map UA-CH headers', function () { assert.strictEqual(os.name, "Linux"); assert.strictEqual(os.version, "x86_64"); }); +}); + +describe('Map UA-CH JS', function () { + it('Can read client hints from browser', async function () { + let ua = new UAParser(); + let browser = await ua.getBrowser().withClientHints(); + // TODO : create tests + }); }); \ No newline at end of file From 60d3a2fbbc0670cdb8c3375d1eed47757604e3b6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 12 Mar 2023 16:24:38 +0700 Subject: [PATCH 038/388] Create test for client hints in browser context --- src/ua-parser.js | 92 ++++++++++++++++++++++++++---------------------- test/test.js | 62 +++++++++++++++++++++++++++++--- 2 files changed, 106 insertions(+), 48 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 0695044d2..acc6fed07 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -47,22 +47,23 @@ CH_HEADER_MODEL = CH_HEADER + '-model', CH_HEADER_PLATFORM = CH_HEADER + '-platform', CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness']; + CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'], + UA_BROWSER = 'browser', + UA_CPU = 'cpu', + UA_DEVICE = 'device', + UA_ENGINE = 'engine', + UA_OS = 'os', + UA_RESULT = 'result'; var AMAZON = 'Amazon', APPLE = 'Apple', ASUS = 'ASUS', BLACKBERRY = 'BlackBerry', - BROWSER = 'Browser', - CHROME = 'Chrome', - EDGE = 'Edge', - FIREFOX = 'Firefox', GOOGLE = 'Google', HUAWEI = 'Huawei', LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', - OPERA = 'Opera', SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', @@ -70,7 +71,14 @@ XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', ZTE = 'ZTE', + + BROWSER = 'Browser', + CHROME = 'Chrome', + EDGE = 'Edge', + FIREFOX = 'Firefox', + OPERA = 'Opera', FACEBOOK = 'Facebook', + CHROMIUM_OS = 'Chromium OS', MAC_OS = 'Mac OS', WINDOWS = 'Windows'; @@ -385,12 +393,10 @@ cpu : [[ - /(?:(amd|x(?:(?:86|64)[-_])?|wow|win)64)[;\)]/i // AMD64 (x64) + /\b(?:(amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64) ], [[ARCHITECTURE, 'amd64']], [ - /(ia32(?=;))/i // IA32 (quicktime) - ], [[ARCHITECTURE, lowerize]], [ - + /(ia32(?=;))/i, // IA32 (quicktime) /((?:i[346]|x)86)[;\)]/i // IA32 (x86) ], [[ARCHITECTURE, 'ia32']], [ @@ -824,6 +830,9 @@ function UAParserDataCH (uach, isHTTP_UACH) { uach = uach || {}; initialize.call(this, CH_ALL_VALUES); + var setVal = function (val) { + return typeof val === STR_TYPE ? strip(/\"/g, val) : val || undefined; + }; if (isHTTP_UACH) { var toArray = function (header) { if (!header) return undefined; @@ -838,17 +847,14 @@ this.brands = toArray(uach[CH_HEADER]); this.fullVersionList = toArray(uach[CH_HEADER_FULL_VER_LIST]); this.mobile = /\?1/.test(uach[CH_HEADER_MOBILE]); - var setHeader = function (header) { - return header ? strip(/\"/g, header) : undefined; - }; - this.model = setHeader(uach[CH_HEADER_MODEL]); - this.platform = setHeader(uach[CH_HEADER_PLATFORM]); - this.platformVersion = setHeader(uach[CH_HEADER_PLATFORM_VER]); - this.architecture = setHeader(uach[CH_HEADER_ARCH]); - this.bitness = setHeader(uach[CH_HEADER_BITNESS]); + this.model = setVal(uach[CH_HEADER_MODEL]); + this.platform = setVal(uach[CH_HEADER_PLATFORM]); + this.platformVersion = setVal(uach[CH_HEADER_PLATFORM_VER]); + this.architecture = setVal(uach[CH_HEADER_ARCH]); + this.bitness = setVal(uach[CH_HEADER_BITNESS]); } else { for (var prop in uach) { - this[prop] = uach[prop]; + if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = setVal(uach[prop]); } } return this; @@ -894,28 +900,28 @@ return str ? str : UNDEF_TYPE; }; UAParserData.prototype.withClientHints = function () { - if (!NAVIGATOR_UADATA) return; + if (!NAVIGATOR_UADATA) return this; return NAVIGATOR_UADATA .getHighEntropyValues(CH_ALL_VALUES) .then(function (res) { var JS_UACH = new UAParserDataCH(res, false), browser = new UAParserBrowser(ua, rgxMap, JS_UACH).get(), - cpu = new UAParserCPU(ua, rgxMap, JS_UACH).get(), - device = new UAParserDevice(ua, rgxMap, JS_UACH).get(), - engine = new UAParserEngine(ua, rgxMap).get(), - os = new UAParserOS(ua, rgxMap, JS_UACH).get(); + cpu = new UAParserCPU(ua, ((itemType == UA_RESULT) ? rgxMap.cpu : rgxMap), JS_UACH).get(), + device = new UAParserDevice(ua, rgxMap, JS_UACH).get(), + engine = new UAParserEngine(ua, rgxMap).get(), + os = new UAParserOS(ua, rgxMap, JS_UACH).get(); switch (itemType) { - case 'browser': + case UA_BROWSER: return browser; - case 'cpu': + case UA_CPU: return cpu; - case 'device': + case UA_DEVICE: return device; - case 'engine': + case UA_ENGINE: return engine; - case 'os': + case UA_OS: return os; default : return { @@ -950,7 +956,7 @@ UAParserItem.call(this, [ ua, uach, - 'browser', + UA_BROWSER, browserMap, [NAME, VERSION, MAJOR], [VERSION, MAJOR], @@ -988,7 +994,7 @@ UAParserItem.call(this, [ ua, uach, - 'cpu', + UA_CPU, cpuMap, [ARCHITECTURE], [], @@ -1014,7 +1020,7 @@ UAParserItem.call(this, [ ua, uach, - 'device', + UA_DEVICE, deviceMap, [TYPE, MODEL, VENDOR], [], @@ -1049,7 +1055,7 @@ UAParserItem.call(this, [ ua, null, - 'engine', + UA_ENGINE, engineMap, [NAME, VERSION], [VERSION], @@ -1064,7 +1070,7 @@ UAParserItem.call(this, [ ua, uach, - 'os', + UA_OS, osMap, [NAME, VERSION], [VERSION], @@ -1074,7 +1080,7 @@ this.parseCH(); if (!this.get(NAME)) { this.parse(); - if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform != 'Unknown') { + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform && NAVIGATOR_UADATA.platform != 'Unknown') { this.set(NAME, NAVIGATOR_UADATA.platform .replace(/chrome os/i, CHROMIUM_OS) .replace(/macos/i, MAC_OS)); // backward compatibility @@ -1093,8 +1099,8 @@ return this; }; - function UAParserResult (ua, uach) { - UAParserItem.call(this, [ua, uach]); + function UAParserResult (ua, resMap, uach) { + UAParserItem.call(this, [ua, uach, UA_RESULT, resMap]); } UAParserResult.prototype = new UAParserItem(); @@ -1155,14 +1161,14 @@ }; this.getResult = function () { - return new UAParserResult(userAgent, HTTP_UACH) + return new UAParserResult(userAgent, regexMap, HTTP_UACH) .set('ua', userAgent) .set('ua_ch', HTTP_UACH) - .set('browser', this.getBrowser()) - .set('cpu', this.getCPU()) - .set('device', this.getDevice()) - .set('engine', this.getEngine()) - .set('os', this.getOS()) + .set(UA_BROWSER, this.getBrowser()) + .set(UA_CPU, this.getCPU()) + .set(UA_DEVICE, this.getDevice()) + .set(UA_ENGINE, this.getEngine()) + .set(UA_OS, this.getOS()) .get(); }; diff --git a/test/test.js b/test/test.js index 30f26d108..329149741 100644 --- a/test/test.js +++ b/test/test.js @@ -402,10 +402,62 @@ describe('Map UA-CH headers', function () { }); }); -describe('Map UA-CH JS', function () { - it('Can read client hints from browser', async function () { - let ua = new UAParser(); - let browser = await ua.getBrowser().withClientHints(); - // TODO : create tests +describe('Map UA-CH JS', () => { + + it('does not throw when using withClientHints() in non-supported environment', () => { + assert.doesNotThrow(() => { + new UAParser().getResult().withClientHints(); + }); + }); + + window = { navigator : { userAgent : '' , userAgentData : new NavigatorUAData() } }; + function NavigatorUAData () { + this.mobile = false; + this.platform = ''; + this.brands = [{ brand : 'A Chromium-based Browser', version : '1' }]; + this.getHighEntropyValues = (values) => { + return new Promise((resolve) => { + const result = { + brands : [{ brand : 'A Chromium-based Browser', version : '1' }], + fullVersionList : [{ brand : 'A Chromium-based Browser', version : '1.2.3' }], + architecture : 'x86', + bitness : '64', + mobile : true, + model : 'Galaxy S3', + platform : 'Android', + platformVersion : '1000' + }; + resolve(result); + }); + } + }; + delete require.cache[require.resolve('./../src/ua-parser')]; + const UAParserWithWindow = require('./../src/ua-parser'); + + it('Can read client hints from browser', async () => { + + let uap = new UAParserWithWindow() + + let os = await uap.getOS().withClientHints(); + + assert.strictEqual(os.name, 'Android'); + assert.strictEqual(os.is('Android'), true); + assert.strictEqual(os.toString(), 'Android 1000'); + + let result = await uap.getResult().withClientHints(); + + assert.strictEqual(result.browser.name, 'A Chromium-based Browser'); + assert.strictEqual(result.browser.version, '1.2.3'); + assert.strictEqual(result.cpu.architecture, 'amd64'); + assert.strictEqual(result.os.name, 'Android'); + + let result_without_ch = uap.getResult(); + + assert.strictEqual(result_without_ch.browser.name, undefined); + + uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); + assert.strictEqual(uap.getOS().name, "Mac OS"); + + // TODO : create full tests }); }); \ No newline at end of file From f8dde65d54e92246a6ffe7f5a7af9262e87de137 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 14 Mar 2023 23:22:08 +0700 Subject: [PATCH 039/388] Only use user-agent data by default. Must explicitly call `withClientHints()` to also use client-hints data --- src/ua-parser.js | 163 ++++++++++++++++++++++++++--------------------- test/test.js | 146 +++++++++++++++++++++++++++++++++--------- 2 files changed, 208 insertions(+), 101 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index acc6fed07..73e26f88e 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -39,6 +39,11 @@ EMBEDDED = 'embedded', USER_AGENT = 'user-agent', UA_MAX_LENGTH = 350, + BRANDS = 'brands', + FULLVERLIST = 'fullVersionList', + PLATFORM = 'platform', + PLATFORMVER = 'platformVersion', + BITNESS = 'bitness', CH_HEADER = 'sec-ch-ua', CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', @@ -127,6 +132,16 @@ return /^(browser|cpu|device|engine|os)$/.test(prop); } }, + itemListToArray = function (header) { + if (!header) return undefined; + var arr = []; + var tokens = strip(/\\?\"/g, header).split(', '); + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i].split(';v='); + arr[i] = { brand : token[0], version : token[1] }; + } + return arr; + }, lowerize = function (str, rgx) { return typeof(str) === STR_TYPE ? strip((rgx ? new RegExp(rgx, 'i') : EMPTY), str.toLowerCase()) : str; }, @@ -136,6 +151,9 @@ strip = function (pattern, str) { return str.replace(pattern, EMPTY); }, + stripQuotes = function (val) { + return typeof val === STR_TYPE ? strip(/\"/g, val) : val; + }, trim = function (str, len) { if (typeof(str) === STR_TYPE) { str = strip(/^\s\s*/, str); @@ -830,31 +848,21 @@ function UAParserDataCH (uach, isHTTP_UACH) { uach = uach || {}; initialize.call(this, CH_ALL_VALUES); - var setVal = function (val) { - return typeof val === STR_TYPE ? strip(/\"/g, val) : val || undefined; - }; if (isHTTP_UACH) { - var toArray = function (header) { - if (!header) return undefined; - var arr = []; - var tokens = strip(/\\?\"/g, header).split(', '); - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i].split(';v='); - arr[i] = { brand : token[0], version : token[1] }; - } - return arr; - }; - this.brands = toArray(uach[CH_HEADER]); - this.fullVersionList = toArray(uach[CH_HEADER_FULL_VER_LIST]); - this.mobile = /\?1/.test(uach[CH_HEADER_MOBILE]); - this.model = setVal(uach[CH_HEADER_MODEL]); - this.platform = setVal(uach[CH_HEADER_PLATFORM]); - this.platformVersion = setVal(uach[CH_HEADER_PLATFORM_VER]); - this.architecture = setVal(uach[CH_HEADER_ARCH]); - this.bitness = setVal(uach[CH_HEADER_BITNESS]); + initialize.call(this, [ + [BRANDS, itemListToArray(uach[CH_HEADER])], + [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], + [BRANDS, itemListToArray(uach[CH_HEADER])], + [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], + [MODEL, stripQuotes(uach[CH_HEADER_MODEL])], + [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], + [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], + [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], + [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] + ]); } else { for (var prop in uach) { - if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = setVal(uach[prop]); + if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = stripQuotes(uach[prop]); } } return this; @@ -867,6 +875,7 @@ this.rgxMap = data[3]; this.data = (function (data) { var ua = data[0], + uaCH = data[1], itemType = data[2], rgxMap = data[3], init_props = data[4], @@ -900,17 +909,41 @@ return str ? str : UNDEF_TYPE; }; UAParserData.prototype.withClientHints = function () { - if (!NAVIGATOR_UADATA) return this; + if (!NAVIGATOR_UADATA) { + var HTTP_UACH = uaCH; + switch (itemType) { + case UA_BROWSER: + return new UAParserBrowser(ua, rgxMap, HTTP_UACH).parseCH().get(); + case UA_CPU: + return new UAParserCPU(ua, rgxMap, HTTP_UACH).parseCH().get(); + case UA_DEVICE: + return new UAParserDevice(ua, rgxMap, HTTP_UACH).parseCH().get(); + case UA_ENGINE: + return new UAParserEngine(ua, rgxMap).get(); + case UA_OS: + return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get(); + default : + return { + 'ua' : ua, + 'ua_ch' : uaCH, + 'browser' : new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get(), + 'cpu' : new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get(), + 'device' : new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get(), + 'engine' : new UAParserEngine(ua, rgxMap[UA_ENGINE]).get(), + 'os' : new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get() + }; + } + } return NAVIGATOR_UADATA .getHighEntropyValues(CH_ALL_VALUES) .then(function (res) { var JS_UACH = new UAParserDataCH(res, false), - browser = new UAParserBrowser(ua, rgxMap, JS_UACH).get(), - cpu = new UAParserCPU(ua, ((itemType == UA_RESULT) ? rgxMap.cpu : rgxMap), JS_UACH).get(), - device = new UAParserDevice(ua, rgxMap, JS_UACH).get(), + browser = new UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get(), + cpu = new UAParserCPU(ua, ((itemType == UA_RESULT) ? rgxMap[UA_CPU] : rgxMap), JS_UACH).parseCH().get(), + device = new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get(), engine = new UAParserEngine(ua, rgxMap).get(), - os = new UAParserOS(ua, rgxMap, JS_UACH).get(); + os = new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get(); switch (itemType) { case UA_BROWSER: @@ -963,24 +996,21 @@ ' ?browser$', [NAME, VERSION] ]); - this.parseCH(); - if (!this.get(NAME)) { - this.parse(); - // Brave-specific detection - if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { - this.set(NAME, 'Brave'); - } + this.parse(); + // Brave-specific detection + if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); } this.set(MAJOR, majorize(this.get(VERSION))); } UAParserBrowser.prototype = new UAParserItem(); UAParserBrowser.prototype.parseCH = function () { - var brands = this.uaCH.fullVersionList || this.uaCH.brands; + var brands = this.uaCH[FULLVERLIST] || this.uaCH[BRANDS]; if (brands) { for (var i in brands) { var brandName = brands[i].brand, brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) { + if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) { this.set(NAME, strip(GOOGLE+' ', brandName)) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); @@ -1001,16 +1031,13 @@ null, [ARCHITECTURE] ]); - this.parseCH(); - if (!this.get(ARCHITECTURE)) { - this.parse(); - } + this.parse(); } UAParserCPU.prototype = new UAParserItem(); UAParserCPU.prototype.parseCH = function () { - var archName = this.uaCH.architecture; + var archName = this.uaCH[ARCHITECTURE]; if (archName) { - archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY; + archName += (archName && this.uaCH[BITNESS] == '64') ? '64' : EMPTY; rgxMapper.call(this.data, archName, this.rgxMap); } return this; @@ -1027,26 +1054,23 @@ null, [VENDOR, MODEL] ]); - this.parseCH(); - if (!this.get(TYPE) || !this.get(MODEL)) { - this.parse(); - if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.mobile) { - this.set(TYPE, MOBILE); - } - // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { - this.set(MODEL, 'iPad') - .set(TYPE, TABLET); - } + this.parse(); + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); } } UAParserDevice.prototype = new UAParserItem(); UAParserDevice.prototype.parseCH = function () { - if (this.uaCH.mobile) { + if (this.uaCH[MOBILE]) { this.set(TYPE, MOBILE); } - if (this.uaCH.model) { - this.set(MODEL, strip(/\"/g, this.uaCH.model)); + if (this.uaCH[MODEL]) { + this.set(MODEL, this.uaCH[MODEL]); } return this; }; @@ -1077,21 +1101,18 @@ ' ?os$', [NAME, VERSION] ]); - this.parseCH(); - if (!this.get(NAME)) { - this.parse(); - if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform && NAVIGATOR_UADATA.platform != 'Unknown') { - this.set(NAME, NAVIGATOR_UADATA.platform - .replace(/chrome os/i, CHROMIUM_OS) - .replace(/macos/i, MAC_OS)); // backward compatibility - } + this.parse(); + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { + this.set(NAME, NAVIGATOR_UADATA[PLATFORM] + .replace(/chrome os/i, CHROMIUM_OS) + .replace(/macos/i, MAC_OS)); // backward compatibility } } UAParserOS.prototype = new UAParserItem(); UAParserOS.prototype.parseCH = function () { - var osName = this.uaCH.platform; + var osName = this.uaCH[PLATFORM]; if(osName) { - var osVersion = this.uaCH.platformVersion; + var osVersion = this.uaCH[PLATFORMVER]; osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; this.set(NAME, osName) .set(VERSION, osVersion); @@ -1141,23 +1162,23 @@ // public methods this.getBrowser = function () { - return new UAParserBrowser(userAgent, regexMap.browser, HTTP_UACH).get(); + return new UAParserBrowser(userAgent, regexMap[UA_BROWSER], HTTP_UACH).get(); }; this.getCPU = function () { - return new UAParserCPU(userAgent, regexMap.cpu, HTTP_UACH).get(); + return new UAParserCPU(userAgent, regexMap[UA_CPU], HTTP_UACH).get(); }; this.getDevice = function () { - return new UAParserDevice(userAgent, regexMap.device, HTTP_UACH).get(); + return new UAParserDevice(userAgent, regexMap[UA_DEVICE], HTTP_UACH).get(); }; this.getEngine = function () { - return new UAParserEngine(userAgent, regexMap.engine).get(); + return new UAParserEngine(userAgent, regexMap[UA_ENGINE]).get(); }; this.getOS = function () { - return new UAParserOS(userAgent, regexMap.os, HTTP_UACH).get(); + return new UAParserOS(userAgent, regexMap[UA_OS], HTTP_UACH).get(); }; this.getResult = function () { diff --git a/test/test.js b/test/test.js index 329149741..aab97ad91 100644 --- a/test/test.js +++ b/test/test.js @@ -351,54 +351,134 @@ describe('Map UA-CH headers', function () { 'sec-ch-ua-platform-version' : '13', 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' }; - - const headers2 = { - 'sec-ch-ua-mobile' : '?1', - 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + + let uap = UAParser(headers).withClientHints(); + let browser = new UAParser(headers).getBrowser().withClientHints(); + let cpu = new UAParser(headers).getCPU().withClientHints(); + let device = new UAParser(headers).getDevice().withClientHints(); + let engine = new UAParser(headers).getEngine().withClientHints(); + let os = new UAParser(headers).getOS().withClientHints(); + + let ua_ch = { + "architecture": "ARM", + "bitness": "64", + "brands": [ + { + "brand": "Chromium", + "version": "93" + }, + { + "brand": "Google Chrome", + "version": "93" + }, + { + "brand": " Not;A Brand", + "version": "99" + } + ], + "fullVersionList": [ + { + "brand": "Chromium", + "version": "93.0.1.2" + }, + { + "brand": "Google Chrome", + "version": "93.0.1.2" + }, + { + "brand": " Not;A Brand", + "version": "99.0.1.2" + } + ], + "mobile": true, + "model": "Pixel 99", + "platform": "Windows", + "platformVersion": "13" }; - let uap = UAParser(headers); - let browser = uap.browser; - let cpu = uap.cpu; - let device = uap.device; - let engine = uap.engine; - let os = uap.os; - - it('Can read from client-hints headers', function () { + it('Can read from client-hints headers using `withClientHints()`', function () { + assert.deepEqual(uap.ua_ch, ua_ch); + assert.strictEqual(uap.ua, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "93.0.1.2"); + assert.strictEqual(uap.browser.major, "93"); assert.strictEqual(browser.name, "Chrome"); assert.strictEqual(browser.version, "93.0.1.2"); assert.strictEqual(browser.major, "93"); + assert.strictEqual(uap.cpu.architecture, "arm64"); assert.strictEqual(cpu.architecture, "arm64"); + assert.strictEqual(uap.device.type, "mobile"); + assert.strictEqual(uap.device.model, "Pixel 99"); + assert.strictEqual(uap.device.vendor, undefined); assert.strictEqual(device.type, "mobile"); assert.strictEqual(device.model, "Pixel 99"); assert.strictEqual(device.vendor, undefined); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '110.0.0.0'); assert.strictEqual(engine.name, 'Blink'); assert.strictEqual(engine.version, '110.0.0.0'); + assert.strictEqual(uap.os.name, "Windows"); + assert.strictEqual(uap.os.version, "11"); assert.strictEqual(os.name, "Windows"); assert.strictEqual(os.version, "11"); }); - it('Can read from user-agent header', function () { + it('Only read from user-agent header when called without `withClientHints()`', function () { + + uap = UAParser(headers); + browser = new UAParser(headers).getBrowser(); + cpu = new UAParser(headers).getCPU(); + device = new UAParser(headers).getDevice(); + engine = new UAParser(headers).getEngine(); + os = new UAParser(headers).getOS(); + + assert.deepEqual(uap.ua_ch, ua_ch); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "110.0.0.0"); + assert.strictEqual(uap.browser.major, "110"); + assert.strictEqual(uap.cpu.architecture, "amd64"); + assert.strictEqual(uap.device.type, undefined); + assert.strictEqual(uap.device.model, undefined); + assert.strictEqual(uap.device.vendor, undefined); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '110.0.0.0'); + assert.strictEqual(uap.os.name, "Linux"); + assert.strictEqual(uap.os.version, "x86_64"); + }); - uap = UAParser(headers2); - browser = uap.browser; - cpu = uap.cpu; - device = uap.device; - engine = uap.engine; - os = uap.os; + it('Fallback to user-agent header when using `withClientHints()` but found no client hints-related headers', function () { - assert.strictEqual(browser.name, "Chrome"); - assert.strictEqual(browser.version, "110.0.0.0"); - assert.strictEqual(browser.major, "110"); - assert.strictEqual(cpu.architecture, "amd64"); - assert.strictEqual(device.type, "mobile"); - assert.strictEqual(device.model, undefined); - assert.strictEqual(device.vendor, undefined); - assert.strictEqual(engine.name, 'Blink'); - assert.strictEqual(engine.version, '110.0.0.0'); - assert.strictEqual(os.name, "Linux"); - assert.strictEqual(os.version, "x86_64"); + const headers2 = { + 'sec-ch-ua-mobile' : '?1', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; + + uap = UAParser(headers2).withClientHints(); + + ua_ch = { + "architecture": undefined, + "bitness": undefined, + "brands": undefined, + "fullVersionList": undefined, + "mobile": true, + "model": undefined, + "platform": undefined, + "platformVersion": undefined + }; + + assert.deepEqual(uap.ua_ch, ua_ch); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "110.0.0.0"); + assert.strictEqual(uap.browser.major, "110"); + assert.strictEqual(uap.cpu.architecture, "amd64"); + assert.strictEqual(uap.device.type, "mobile"); + assert.strictEqual(uap.device.model, undefined); + assert.strictEqual(uap.device.vendor, undefined); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '110.0.0.0'); + assert.strictEqual(uap.os.name, "Linux"); + assert.strictEqual(uap.os.version, "x86_64"); }); }); @@ -451,6 +531,12 @@ describe('Map UA-CH JS', () => { assert.strictEqual(result.cpu.architecture, 'amd64'); assert.strictEqual(result.os.name, 'Android'); + await uap.getDevice().withClientHints().then((device) => { + assert.strictEqual(device.type, 'mobile'); + assert.strictEqual(device.vendor, undefined); + assert.strictEqual(device.model, 'Galaxy S3'); + }); + let result_without_ch = uap.getResult(); assert.strictEqual(result_without_ch.browser.name, undefined); From c78346d3b4b6ead2a558d835f439eda6115d7747 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 15 Mar 2023 23:22:34 +0700 Subject: [PATCH 040/388] Returns `withClientHints()` as `Thenable` in nodejs / non-client-hints browsers --- readme.md | 25 +++++++++++-------- src/ua-parser.js | 65 ++++++++++++++++++++++++++---------------------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/readme.md b/readme.md index c37673527..93f5c8159 100644 --- a/readme.md +++ b/readme.md @@ -33,6 +33,11 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro

UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.

This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).

Visit ↗ 51Degrees UAParser to get started.

+ + +↗ Become a sponsor + + @@ -41,13 +46,11 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro # Documentation ### UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)]) -In The Browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in nodejs for the function to work. -Usually you can find the user agent in: -`request.headers["user-agent"]`. +In the browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in' nodejs for the function to work. Usually you can find the user agent in: `request.headers["user-agent"]`. ## Constructor -When you call `UAParser` with the `new` keyword `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string. +When you call `UAParser` with the `new` keyword, `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string. Like so: * `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])` ```js @@ -183,7 +186,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal * set UA string to be parsed * returns current instance -#### * `is()` utility `since@1.1` +#### * `is():boolean` utility `since@1.1` ```js // Is just a shorthand to check whether specified item has a property with equals value (case-insensitive) @@ -247,7 +250,7 @@ let engine = uap.getEngine(); engine.is("Blink"); // true ``` -#### * `toString()` utility `since@1.1` +#### * `toString():string` utility `since@1.1` ```js // Retrieve full-name values as a string @@ -288,9 +291,9 @@ engine.version; // "28.0.1500.95" engine.toString(); // "Blink 28.0.1500.95" ``` -#### * `withClientHints()` `since@1.1` +#### * `withClientHints():Promise|Thenable` `since@1.1` -Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`. +Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`. In nodejs-environment / browser-environment with non-secure context or without client-hints support (basically anything that's not chromium-based) this will return the updated data as a `Thenable` (can be chained with `then()`). ```js (async function () { @@ -451,11 +454,13 @@ http.createServer(function (req, res) { var ua = uap(req.headers['user-agent']); /* // BEGIN since@1.1 - you can also pass client-hints data to UAParser - + + // note: only works in secure context (https:// or localhost or file://) + var getHighEntropyValues = 'Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness'; res.setHeader('Accept-CH', getHighEntropyValues); res.setHeader('Critical-CH', getHighEntropyValues); - + var ua = uap(req.headers); // END since@1.1 */ diff --git a/src/ua-parser.js b/src/ua-parser.js index 73e26f88e..c017e8c7e 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -909,7 +909,10 @@ return str ? str : UNDEF_TYPE; }; UAParserData.prototype.withClientHints = function () { + + // nodejs / non-client-hints browsers if (!NAVIGATOR_UADATA) { + var HTTP_UACH = uaCH; switch (itemType) { case UA_BROWSER: @@ -923,52 +926,54 @@ case UA_OS: return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get(); default : - return { - 'ua' : ua, - 'ua_ch' : uaCH, - 'browser' : new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get(), - 'cpu' : new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get(), - 'device' : new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get(), - 'engine' : new UAParserEngine(ua, rgxMap[UA_ENGINE]).get(), - 'os' : new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get() - }; + return new UAParserResult(ua, rgxMap, HTTP_UACH) + .set('ua', ua) + .set('ua_ch', uaCH) + .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get()) + .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get()) + .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get()) + .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get()) + .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get()) + .get(); } } + + // browsers based on chromium 85+ return NAVIGATOR_UADATA .getHighEntropyValues(CH_ALL_VALUES) .then(function (res) { - var JS_UACH = new UAParserDataCH(res, false), - browser = new UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get(), - cpu = new UAParserCPU(ua, ((itemType == UA_RESULT) ? rgxMap[UA_CPU] : rgxMap), JS_UACH).parseCH().get(), - device = new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get(), - engine = new UAParserEngine(ua, rgxMap).get(), - os = new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get(); - + var JS_UACH = new UAParserDataCH(res, false); switch (itemType) { case UA_BROWSER: - return browser; + return UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get(); case UA_CPU: - return cpu; + return new UAParserCPU(ua, rgxMap, JS_UACH).parseCH().get(); case UA_DEVICE: - return device; + return new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get(); case UA_ENGINE: - return engine; + return new UAParserEngine(ua, rgxMap).get(); case UA_OS: - return os; + return new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get(); default : - return { - 'ua' : ua, - 'ua_ch' : JS_UACH, - 'browser' : browser, - 'cpu' : cpu, - 'device' : device, - 'engine' : engine, - 'os' : os - }; + return new UAParserResult(ua, rgxMap, JS_UACH) + .set('ua', ua) + .set('ua_ch', JS_UACH) + .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], JS_UACH).parseCH().get()) + .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], JS_UACH).parseCH().get()) + .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], JS_UACH).parseCH().get()) + .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get()) + .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], JS_UACH).parseCH().get()) + .get(); } }); }; + if (!NAVIGATOR_UADATA) { + UAParserData.prototype.then = function (cb) { + cb(this); + return this; + }; + } return new UAParserData(); })(data); } From 41f8d76968128659624e599822239ac382968808 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 18 Mar 2023 10:38:03 +0700 Subject: [PATCH 041/388] Remove `is()` & `toString()` prototype from `getResult()` data --- src/ua-parser.js | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index c017e8c7e..42ec24f16 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -886,28 +886,6 @@ function UAParserData () { initialize.call(this, init_props); } - UAParserData.prototype.is = function (strToCheck) { - var is = false; - for (var i in this) { - if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { - is = true; - if (strToCheck != UNDEF_TYPE) break; - } else if (strToCheck == UNDEF_TYPE && is) { - is = !is; - break; - } - } - return is; - }; - UAParserData.prototype.toString = function () { - var str = EMPTY; - for (var i in toString_props) { - if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { - str += (str ? ' ' : EMPTY) + this[toString_props[i]]; - } - } - return str ? str : UNDEF_TYPE; - }; UAParserData.prototype.withClientHints = function () { // nodejs / non-client-hints browsers @@ -968,6 +946,30 @@ } }); }; + if(itemType != UA_RESULT) { + UAParserData.prototype.is = function (strToCheck) { + var is = false; + for (var i in this) { + if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; + } + } + return is; + }; + UAParserData.prototype.toString = function () { + var str = EMPTY; + for (var i in toString_props) { + if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[toString_props[i]]; + } + } + return str ? str : UNDEF_TYPE; + }; + } if (!NAVIGATOR_UADATA) { UAParserData.prototype.then = function (cb) { cb(this); From 6821276669e299b6d23a595fb60f386569955f09 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 18 Mar 2023 10:41:54 +0700 Subject: [PATCH 042/388] Fix #218 #491 #517 #518 - introduce breaking changes: "Mac OS" => "macOS", "Chromium OS" => "Chrome OS" --- src/ua-parser.js | 10 +++------- test/os-test.json | 12 ++++++------ test/test.js | 7 ++++--- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 42ec24f16..3f703533c 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -84,8 +84,6 @@ OPERA = 'Opera', FACEBOOK = 'Facebook', - CHROMIUM_OS = 'Chromium OS', - MAC_OS = 'Mac OS', WINDOWS = 'Windows'; var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ? @@ -783,7 +781,7 @@ ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS - ], [[NAME, MAC_OS], [VERSION, /_/g, '.']], [ + ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ // Mobile OSes /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS @@ -809,7 +807,7 @@ /crkey\/([\d\.]+)/i // Google Chromecast ], [VERSION, [NAME, CHROME+'cast']], [ /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS - ], [[NAME, CHROMIUM_OS], VERSION],[ + ], [[NAME, "Chrome OS"], VERSION],[ // Smart TVs /panasonic;(viera)/i, // Panasonic Viera @@ -1110,9 +1108,7 @@ ]); this.parse(); if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { - this.set(NAME, NAVIGATOR_UADATA[PLATFORM] - .replace(/chrome os/i, CHROMIUM_OS) - .replace(/macos/i, MAC_OS)); // backward compatibility + this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); } } UAParserOS.prototype = new UAParserItem(); diff --git a/test/os-test.json b/test/os-test.json index d97c7ffce..f720a1aa6 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -670,7 +670,7 @@ "ua" : "Mozilla/5.0 (X11; CrOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.0.0 Safari/537.36", "expect" : { - "name" : "Chromium OS", + "name" : "Chrome OS", "version" : "undefined" } }, @@ -679,7 +679,7 @@ "ua" : "Mozilla/5.0 (X11; CrOS x86_64 10575.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", "expect" : { - "name" : "Chromium OS", + "name" : "Chrome OS", "version" : "10575.58.0" } }, @@ -796,7 +796,7 @@ "ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)", "expect" : { - "name" : "Mac OS", + "name" : "macOS", "version" : "undefined" } }, @@ -805,7 +805,7 @@ "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", "expect" : { - "name" : "Mac OS", + "name" : "macOS", "version" : "x.y" } }, @@ -814,7 +814,7 @@ "ua" : "Mozilla/5.0 (Macintosh; PPC Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", "expect" : { - "name" : "Mac OS", + "name" : "macOS", "version" : "x.y" } }, @@ -823,7 +823,7 @@ "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36", "expect" : { - "name" : "Mac OS", + "name" : "macOS", "version" : "10.6.8" } }, diff --git a/test/test.js b/test/test.js index aab97ad91..39e948e1d 100644 --- a/test/test.js +++ b/test/test.js @@ -229,11 +229,12 @@ describe('is() utility method', function () { it('Should get result after reassignment', function () { uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); - assert.strictEqual(uap.getOS().name, "Mac OS"); + assert.strictEqual(uap.getOS().name, "macOS"); assert.strictEqual(uap.getOS().is("Mac OS"), true); - assert.strictEqual(uap.getOS().is("M ac"), false); assert.strictEqual(uap.getOS().is("macOS"), true); assert.strictEqual(uap.getOS().is("mac OS"), true); + + assert.strictEqual(uap.getOS().is("M ac"), false); assert.strictEqual(uap.getOS().is("M a c "), false); assert.strictEqual(uap.getOS().is("Mac OS OS"), false); assert.strictEqual(uap.getOS().is("Mac OS X"), false); @@ -542,7 +543,7 @@ describe('Map UA-CH JS', () => { assert.strictEqual(result_without_ch.browser.name, undefined); uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); - assert.strictEqual(uap.getOS().name, "Mac OS"); + assert.strictEqual(uap.getOS().name, "macOS"); // TODO : create full tests }); From 31f94f3a51b2a04a7d4bb7bb8039ab51d178ca17 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 18 Mar 2023 17:04:43 +0700 Subject: [PATCH 043/388] Fix #441 #591 - Generate ESM version of main CJS file at build time --- dist/ua-parser.mjs | 1219 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 10 +- readme.md | 6 + 3 files changed, 1233 insertions(+), 2 deletions(-) create mode 100644 dist/ua-parser.mjs diff --git a/dist/ua-parser.mjs b/dist/ua-parser.mjs new file mode 100644 index 000000000..7d3aafd1e --- /dev/null +++ b/dist/ua-parser.mjs @@ -0,0 +1,1219 @@ +// Generated ESM version of UAParser.js +// Source file: /src/ua-parser.js + +///////////////////////////////////////////////////////////////////////////////// +/* UAParser.js v1.1.34 + Copyright © 2012-2023 Faisal Salman + MIT License *//* + Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. + Supports browser & node.js environment. + Demo : https://faisalman.github.io/ua-parser-js + Source : https://github.com/faisalman/ua-parser-js */ +///////////////////////////////////////////////////////////////////////////////// + +const window = undefined; + + ////////////// + // Constants + ///////////// + + + var LIBVERSION = '1.1.34', + EMPTY = '', + UNKNOWN = '?', + FUNC_TYPE = 'function', + UNDEF_TYPE = 'undefined', + OBJ_TYPE = 'object', + STR_TYPE = 'string', + MAJOR = 'major', + MODEL = 'model', + NAME = 'name', + TYPE = 'type', + VENDOR = 'vendor', + VERSION = 'version', + ARCHITECTURE= 'architecture', + CONSOLE = 'console', + MOBILE = 'mobile', + TABLET = 'tablet', + SMARTTV = 'smarttv', + WEARABLE = 'wearable', + EMBEDDED = 'embedded', + USER_AGENT = 'user-agent', + UA_MAX_LENGTH = 350, + BRANDS = 'brands', + FULLVERLIST = 'fullVersionList', + PLATFORM = 'platform', + PLATFORMVER = 'platformVersion', + BITNESS = 'bitness', + CH_HEADER = 'sec-ch-ua', + CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', + CH_HEADER_ARCH = CH_HEADER + '-arch', + CH_HEADER_BITNESS = CH_HEADER + '-bitness', + CH_HEADER_MOBILE = CH_HEADER + '-mobile', + CH_HEADER_MODEL = CH_HEADER + '-model', + CH_HEADER_PLATFORM = CH_HEADER + '-platform', + CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', + CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'], + UA_BROWSER = 'browser', + UA_CPU = 'cpu', + UA_DEVICE = 'device', + UA_ENGINE = 'engine', + UA_OS = 'os', + UA_RESULT = 'result'; + + var AMAZON = 'Amazon', + APPLE = 'Apple', + ASUS = 'ASUS', + BLACKBERRY = 'BlackBerry', + GOOGLE = 'Google', + HUAWEI = 'Huawei', + LG = 'LG', + MICROSOFT = 'Microsoft', + MOTOROLA = 'Motorola', + SAMSUNG = 'Samsung', + SHARP = 'Sharp', + SONY = 'Sony', + SWISS = 'Swiss', + XIAOMI = 'Xiaomi', + ZEBRA = 'Zebra', + ZTE = 'ZTE', + + BROWSER = 'Browser', + CHROME = 'Chrome', + EDGE = 'Edge', + FIREFOX = 'Firefox', + OPERA = 'Opera', + FACEBOOK = 'Facebook', + + WINDOWS = 'Windows'; + + var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ? + window.navigator : + undefined, + NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ? + NAVIGATOR.userAgentData : + undefined; + + /////////// + // Helper + ////////// + + var extend = function (regexes, extensions) { + var mergedRegexes = {}; + for (var i in regexes) { + mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; + } + return mergedRegexes; + }, + enumerize = function (arr) { + var enums = {}; + for (var i=0; i 0) { + if (q.length === 2) { + if (typeof q[1] == FUNC_TYPE) { + // assign modified match + this[q[0]] = q[1].call(this, match); + } else { + // assign given value, ignore regex match + this[q[0]] = q[1]; + } + } else if (q.length === 3) { + // check whether function or regex + if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { + // call function (usually string mapper) + this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; + } else { + // sanitize match using given regex + this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; + } + } else if (q.length === 4) { + this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; + } + } else { + this[q] = match ? match : undefined; + } + } + } + } + i += 2; + } + }, + + strMapper = function (str, map) { + + for (var i in map) { + // check if current value is array + if (typeof map[i] === OBJ_TYPE && map[i].length > 0) { + for (var j = 0; j < map[i].length; j++) { + if (has(map[i][j], str)) { + return (i === UNKNOWN) ? undefined : i; + } + } + } else if (has(map[i], str)) { + return (i === UNKNOWN) ? undefined : i; + } + } + return str; + }; + + /////////////// + // String map + ////////////// + + // Safari < 3.0 + var oldSafariMap = { + '1.0' : '/8', + '1.2' : '/1', + '1.3' : '/3', + '2.0' : '/412', + '2.0.2' : '/416', + '2.0.3' : '/417', + '2.0.4' : '/419', + '?' : '/' + }, + windowsVersionMap = { + 'ME' : '4.90', + 'NT 3.11' : 'NT3.51', + 'NT 4.0' : 'NT4.0', + '2000' : 'NT 5.0', + 'XP' : ['NT 5.1', 'NT 5.2'], + 'Vista' : 'NT 6.0', + '7' : 'NT 6.1', + '8' : 'NT 6.2', + '8.1' : 'NT 6.3', + '10' : ['NT 6.4', 'NT 10.0'], + 'RT' : 'ARM' + }, + archEquivalenceMap = { + 'amd64' : ['x86-64', 'x64'], + 'ia32' : ['x86'] + }; + + ////////////// + // Regex map + ///////////// + + var regexes = { + + browser : [[ + + /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS + ], [VERSION, [NAME, 'Chrome']], [ + /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge + ], [VERSION, [NAME, 'Edge']], [ + + // Presto based + /(opera mini)\/([-\w\.]+)/i, // Opera Mini + /(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet + /(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i // Opera + ], [NAME, VERSION], [ + /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0 + ], [VERSION, [NAME, OPERA+' Mini']], [ + /\bopr\/([\w\.]+)/i // Opera Webkit + ], [VERSION, [NAME, OPERA]], [ + + // Mixed + /(kindle)\/([\w\.]+)/i, // Kindle + /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer + // Trident based + /(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser + /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser + /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer + + // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ + /(weibo)__([\d\.]+)/i // Weibo + ], [NAME, VERSION], [ + /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser + ], [VERSION, [NAME, 'UC'+BROWSER]], [ + /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser + /\bqbcore\/([\w\.]+).+microm/i + ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [ + /micromessenger\/([\w\.]+)/i // WeChat + ], [VERSION, [NAME, 'WeChat']], [ + /konqueror\/([\w\.]+)/i // Konqueror + ], [VERSION, [NAME, 'Konqueror']], [ + /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11 + ], [VERSION, [NAME, 'IE']], [ + /yabrowser\/([\w\.]+)/i // Yandex + ], [VERSION, [NAME, 'Yandex']], [ + /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser + ], [[NAME, /(.+)/, '$1 Secure '+BROWSER], VERSION], [ + /\bfocus\/([\w\.]+)/i // Firefox Focus + ], [VERSION, [NAME, FIREFOX+' Focus']], [ + /\bopt\/([\w\.]+)/i // Opera Touch + ], [VERSION, [NAME, OPERA+' Touch']], [ + /coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser + ], [VERSION, [NAME, 'Coc Coc']], [ + /dolfin\/([\w\.]+)/i // Dolphin + ], [VERSION, [NAME, 'Dolphin']], [ + /coast\/([\w\.]+)/i // Opera Coast + ], [VERSION, [NAME, OPERA+' Coast']], [ + /miuibrowser\/([\w\.]+)/i // MIUI Browser + ], [VERSION, [NAME, 'MIUI '+BROWSER]], [ + /fxios\/([-\w\.]+)/i // Firefox for iOS + ], [VERSION, [NAME, FIREFOX]], [ + /\bqihu|(qi?ho?o?|360)browser/i // 360 + ], [[NAME, '360 '+BROWSER]], [ + /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i + ], [[NAME, /(.+)/, '$1 '+BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser + /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon + ], [[NAME, /_/g, ' '], VERSION], [ + /(electron)\/([\w\.]+) safari/i, // Electron-based App + /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla + /m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser + ], [NAME, VERSION], [ + /(metasr)[\/ ]?([\w\.]+)/i, // SouGouBrowser + /(lbbrowser)/i, // LieBao Browser + /\[(linkedin)app\]/i // LinkedIn App for iOS & Android + ], [NAME], [ + + // WebView + /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android + ], [[NAME, FACEBOOK], VERSION], [ + /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App + /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp + /safari (line)\/([\w\.]+)/i, // Line App for iOS + /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android + /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram + ], [NAME, VERSION], [ + /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS + ], [VERSION, [NAME, 'GSA']], [ + + /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless + ], [VERSION, [NAME, CHROME+' Headless']], [ + + / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView + ], [[NAME, CHROME+' WebView'], VERSION], [ + + /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser + ], [VERSION, [NAME, 'Android '+BROWSER]], [ + + /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia + ], [NAME, VERSION], [ + + /version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i // Mobile Safari + ], [VERSION, [NAME, 'Mobile Safari']], [ + /version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i // Safari & Safari Mobile + ], [VERSION, NAME], [ + /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 + ], [NAME, [VERSION, strMapper, oldSafariMap]], [ + + /(webkit|khtml)\/([\w\.]+)/i + ], [NAME, VERSION], [ + + // Gecko based + /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape + ], [[NAME, 'Netscape'], VERSION], [ + /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality + ], [VERSION, [NAME, FIREFOX+' Reality']], [ + /ekiohf.+(flow)\/([\w\.]+)/i, // Flow + /(swiftfox)/i, // Swiftfox + /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, + // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar + /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, + // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix + /(firefox)\/([\w\.]+)/i, // Other Firefox-based + /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla + + // Other + /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser + /(links) \(([\w\.]+)/i, // Links + /panasonic;(viera)/i // Panasonic Viera + ], [NAME, VERSION], [ + + /(cobalt)\/([\w\.]+)/i // Cobalt + ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] + ], + + cpu : [[ + + /\b(?:(amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64) + ], [[ARCHITECTURE, 'amd64']], [ + + /(ia32(?=;))/i, // IA32 (quicktime) + /((?:i[346]|x)86)[;\)]/i // IA32 (x86) + ], [[ARCHITECTURE, 'ia32']], [ + + /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 + ], [[ARCHITECTURE, 'arm64']], [ + + /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF + ], [[ARCHITECTURE, 'armhf']], [ + + // PocketPC mistakenly identified as PowerPC + /windows (ce|mobile); ppc;/i + ], [[ARCHITECTURE, 'arm']], [ + + /((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i // PowerPC + ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ + + /(sun4\w)[;\)]/i // SPARC + ], [[ARCHITECTURE, 'sparc']], [ + + /((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i + // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC + ], [[ARCHITECTURE, lowerize]] + ], + + device : [[ + + ////////////////////////// + // MOBILES & TABLETS + ///////////////////////// + + // Samsung + /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [ + /\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, + /samsung[- ]([-\w]+)/i, + /sec-(sgh\w+)/i + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ + + // Apple + /\((ip(?:hone|od)[\w ]*);/i // iPod/iPhone + ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [ + /\((ipad);[-\w\),; ]+apple/i, // iPad + /applecoremedia\/[\w\.]+ \((ipad)/i, + /\b(ipad)\d\d?,\d\d?[;\]].+ios/i + ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [ + /(macintosh);/i + ], [MODEL, [VENDOR, APPLE]], [ + + // Sharp + /\b(sh-?[altvz]?\d\d[a-ekm]?)/i + ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ + + // Huawei + /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i + ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ + /(?:huawei|honor)([-\w ]+)[;\)]/i, + /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i + ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ + + // Xiaomi + /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO + /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models + /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi + /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi + /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi + ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets + ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ + + // OPPO + /; (\w+) bui.+ oppo/i, + /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i + ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ + + // Vivo + /vivo (\w+)(?: bui|\))/i, + /\b(v[12]\d{3}\w?[at])(?: bui|;)/i + ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [ + + // Realme + /\b(rmx[12]\d{3})(?: bui|;|\))/i + ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ + + // Motorola + /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, + /\bmot(?:orola)?[- ](\w*)/i, + /((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ + /\b(mz60\d|xoom[2 ]{0,2}) build\//i + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ + + // LG + /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i + ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ + /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, + /\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i, + /\blg-?([\d\w]+) bui/i + ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ + + // Lenovo + /(ideatab[-\w ]+)/i, + /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i + ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [ + + // Nokia + /(?:maemo|nokia).*(n900|lumia \d+)/i, + /nokia[-_ ]?([-\w\.]*)/i + ], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [ + + // Google + /(pixel c)\b/i // Google Pixel C + ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ + /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel + ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ + + // Sony + /droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i + ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [ + /sony tablet [ps]/i, + /\b(?:sony)?sgp\w+(?: bui|\))/i + ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [ + + // OnePlus + / (kb2005|in20[12]5|be20[12][59])\b/i, + /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i + ], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [ + + // Amazon + /(alexa)webm/i, + /(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk + /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD + ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [ + /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone + ], [[MODEL, /(.+)/g, 'Fire Phone $1'], [VENDOR, AMAZON], [TYPE, MOBILE]], [ + + // BlackBerry + /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook + ], [MODEL, VENDOR, [TYPE, TABLET]], [ + /\b((?:bb[a-f]|st[hv])100-\d)/i, + /\(bb10; (\w+)/i // BlackBerry 10 + ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [ + + // Asus + /(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i + ], [MODEL, [VENDOR, ASUS], [TYPE, TABLET]], [ + / (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i + ], [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], [ + + // HTC + /(nexus 9)/i // HTC Nexus 9 + ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [ + /(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC + + // ZTE + /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i, + /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony + ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ + + // Acer + /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i + ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [ + + // Meizu + /droid.+; (m[1-5] note) bui/i, + /\bmz-([-\w]{2,})/i + ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [ + + // MIXED + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron + /(hp) ([\w ]+\w)/i, // HP iPAQ + /(asus)-?(\w+)/i, // Asus + /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia + /(lenovo)[-_ ]?([-\w]+)/i, // Lenovo + /(jolla)/i, // Jolla + /(oppo) ?([\w ]+) bui/i // OPPO + ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + + /(kobo)\s(ereader|touch)/i, // Kobo + /(archos) (gamepad2?)/i, // Archos + /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad + /(kindle)\/([\w\.]+)/i, // Kindle + /(nook)[\w ]+build\/(\w+)/i, // Nook + /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak + /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets + /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets + /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets + /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(surface duo)/i // Surface Duo + ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [ + /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone + ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [ + /(u304aa)/i // AT&T + ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ + /\bsie-(\w*)/i // Siemens + ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ + /\b(rct\w+) b/i // RCA Tablets + ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ + /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets + ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ + /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet + ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ + /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet + ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ + /\b(tm\d{3}\w+) b/i + ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ + /\b(k88) b/i // ZTE K Series Tablet + ], [MODEL, [VENDOR, ZTE], [TYPE, TABLET]], [ + /\b(nx\d{3}j) b/i // ZTE Nubia + ], [MODEL, [VENDOR, ZTE], [TYPE, MOBILE]], [ + /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile + ], [MODEL, [VENDOR, SWISS], [TYPE, MOBILE]], [ + /\b(zur\d{3}) b/i // Swiss ZUR Tablet + ], [MODEL, [VENDOR, SWISS], [TYPE, TABLET]], [ + /\b((zeki)?tb.*\b) b/i // Zeki Tablets + ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ + /\b([yr]\d{2}) b/i, + /\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet + ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [ + /\b(ns-?\w{0,9}) b/i // Insignia Tablets + ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ + /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets + ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ + /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones + ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ + /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones + ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ + /\b(ph-1) /i // Essential PH-1 + ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ + /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets + ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ + /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets + ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ + /\btu_(1491) b/i // Rotor Tablets + ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [ + /(shield[\w ]+) b/i // Nvidia Shield Tablets + ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [ + /(sprint) (\w+)/i // Sprint Phones + ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + /(kin\.[onetw]{3})/i // Microsoft Kin + ], [[MODEL, /\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [ + /droid.+; ([c6]+|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra + ], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [ + /droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i + ], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [ + + /////////////////// + // SMARTTVS + /////////////////// + + /smart-tv.+(samsung)/i // Samsung + ], [VENDOR, [TYPE, SMARTTV]], [ + /hbbtv.+maple;(\d+)/i + ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV + ], [[VENDOR, LG], [TYPE, SMARTTV]], [ + /(apple) ?tv/i // Apple TV + ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ + /crkey/i // Google Chromecast + ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /droid.+aft(\w)( bui|\))/i // Fire TV + ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ + /\(dtv[\);].+(aquos)/i, + /(aquos-tv[\w ]+)\)/i // Sharp + ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ + /(bravia[\w ]+)( bui|\))/i // Sony + ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ + /(mitv-\w{5}) bui/i // Xiaomi + ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ + /Hbbtv.*(technisat) (.*);/i // TechniSAT + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ + /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku + /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices + ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors + ], [[TYPE, SMARTTV]], [ + + /////////////////// + // CONSOLES + /////////////////// + + /(ouya)/i, // Ouya + /(nintendo) (\w+)/i // Nintendo + ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ + /droid.+; (shield) bui/i // Nvidia + ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [ + /(playstation \w+)/i // Playstation + ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [ + /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox + ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [ + + /////////////////// + // WEARABLES + /////////////////// + + /((pebble))app/i // Pebble + ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ + /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch + ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ + /droid.+; (glass) \d/i // Google Glass + ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ + /droid.+; (wt63?0{2,3})\)/i + ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ + /(quest( 2| pro)?)/i // Oculus Quest + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ + + /////////////////// + // EMBEDDED + /////////////////// + + /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla + ], [VENDOR, [TYPE, EMBEDDED]], [ + + //////////////////// + // MIXED (GENERIC) + /////////////////// + + /droid .+?; ([^;]+?)(?: bui|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors + ], [MODEL, [TYPE, MOBILE]], [ + /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors + ], [MODEL, [TYPE, TABLET]], [ + /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet + ], [[TYPE, TABLET]], [ + /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile + ], [[TYPE, MOBILE]], [ + /(android[-\w\. ]{0,9});.+buil/i // Generic Android Device + ], [MODEL, [VENDOR, 'Generic']] + ], + + engine : [[ + + /windows.+ edge\/([\w\.]+)/i // EdgeHTML + ], [VERSION, [NAME, EDGE+'HTML']], [ + + /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink + ], [VERSION, [NAME, 'Blink']], [ + + /(presto)\/([\w\.]+)/i, // Presto + /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna + /ekioh(flow)\/([\w\.]+)/i, // Flow + /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links + /(icab)[\/ ]([23]\.[\d\.]+)/i // iCab + ], [NAME, VERSION], [ + + /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko + ], [VERSION, NAME] + ], + + os : [[ + + // Windows + /microsoft (windows) (vista|xp)/i // Windows (iTunes) + ], [NAME, VERSION], [ + /(windows) nt 6\.2; (arm)/i, // Windows RT + /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, // Windows Phone + /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i + ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ + /(win(?=3|9|n)|win 9x )([nt\d\.]+)/i + ], [[NAME, WINDOWS], [VERSION, strMapper, windowsVersionMap]], [ + + // iOS/macOS + /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS + /cfnetwork\/.+darwin/i + ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ + /(mac os x) ?([\w\. ]*)/i, + /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS + ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ + + // Mobile OSes + /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS + ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS + /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i, + /(blackberry)\w*\/([\w\.]*)/i, // Blackberry + /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS + /\((series40);/i // Series 40 + ], [NAME, VERSION], [ + /\(bb(10);/i // BlackBerry 10 + ], [VERSION, [NAME, BLACKBERRY]], [ + /(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i // Symbian + ], [VERSION, [NAME, 'Symbian']], [ + /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS + ], [VERSION, [NAME, FIREFOX+' OS']], [ + /web0s;.+rt(tv)/i, + /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS + ], [VERSION, [NAME, 'webOS']], [ + /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS + ], [VERSION, [NAME, 'watchOS']], [ + + // Google Chromecast + /crkey\/([\d\.]+)/i // Google Chromecast + ], [VERSION, [NAME, CHROME+'cast']], [ + /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS + ], [[NAME, "Chrome OS"], VERSION],[ + + // Smart TVs + /panasonic;(viera)/i, // Panasonic Viera + /(netrange)mmh/i, // Netrange + /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV + + // Console + /(nintendo|playstation) (\w+)/i, // Nintendo/Playstation + /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) + + // Other + /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm + /(mint)[\/\(\) ]?(\w*)/i, // Mint + /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire + /(hurd|linux) ?([\w\.]*)/i, // Hurd/Linux + /(gnu) ?([\w\.]*)/i, // GNU + /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly + /(haiku) (\w+)/i // Haiku + ], [NAME, VERSION], [ + /(sunos) ?([\w\.\d]*)/i // Solaris + ], [[NAME, 'Solaris'], VERSION], [ + /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris + /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX + /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX + /(unix) ?([\w\.]*)/i // UNIX + ], [NAME, VERSION] + ] + }; + + ///////////////// + // Constructor + //////////////// + + function UAParserDataCH (uach, isHTTP_UACH) { + uach = uach || {}; + initialize.call(this, CH_ALL_VALUES); + if (isHTTP_UACH) { + initialize.call(this, [ + [BRANDS, itemListToArray(uach[CH_HEADER])], + [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], + [BRANDS, itemListToArray(uach[CH_HEADER])], + [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], + [MODEL, stripQuotes(uach[CH_HEADER_MODEL])], + [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], + [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], + [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], + [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] + ]); + } else { + for (var prop in uach) { + if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = stripQuotes(uach[prop]); + } + } + return this; + } + + function UAParserItem (data) { + if (!data) return; + this.ua = data[0]; + this.uaCH = data[1]; + this.rgxMap = data[3]; + this.data = (function (data) { + var ua = data[0], + uaCH = data[1], + itemType = data[2], + rgxMap = data[3], + init_props = data[4], + is_ignoreProps = data[5], + is_ignoreRgx = data[6], + toString_props = data[7]; + + function UAParserData () { + initialize.call(this, init_props); + } + UAParserData.prototype.withClientHints = function () { + + // nodejs / non-client-hints browsers + if (!NAVIGATOR_UADATA) { + + var HTTP_UACH = uaCH; + switch (itemType) { + case UA_BROWSER: + return new UAParserBrowser(ua, rgxMap, HTTP_UACH).parseCH().get(); + case UA_CPU: + return new UAParserCPU(ua, rgxMap, HTTP_UACH).parseCH().get(); + case UA_DEVICE: + return new UAParserDevice(ua, rgxMap, HTTP_UACH).parseCH().get(); + case UA_ENGINE: + return new UAParserEngine(ua, rgxMap).get(); + case UA_OS: + return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get(); + default : + return new UAParserResult(ua, rgxMap, HTTP_UACH) + .set('ua', ua) + .set('ua_ch', uaCH) + .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get()) + .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get()) + .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get()) + .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get()) + .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get()) + .get(); + } + } + + // browsers based on chromium 85+ + return NAVIGATOR_UADATA + .getHighEntropyValues(CH_ALL_VALUES) + .then(function (res) { + + var JS_UACH = new UAParserDataCH(res, false); + switch (itemType) { + case UA_BROWSER: + return UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get(); + case UA_CPU: + return new UAParserCPU(ua, rgxMap, JS_UACH).parseCH().get(); + case UA_DEVICE: + return new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get(); + case UA_ENGINE: + return new UAParserEngine(ua, rgxMap).get(); + case UA_OS: + return new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get(); + default : + return new UAParserResult(ua, rgxMap, JS_UACH) + .set('ua', ua) + .set('ua_ch', JS_UACH) + .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], JS_UACH).parseCH().get()) + .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], JS_UACH).parseCH().get()) + .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], JS_UACH).parseCH().get()) + .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get()) + .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], JS_UACH).parseCH().get()) + .get(); + } + }); + }; + if(itemType != UA_RESULT) { + UAParserData.prototype.is = function (strToCheck) { + var is = false; + for (var i in this) { + if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; + } + } + return is; + }; + UAParserData.prototype.toString = function () { + var str = EMPTY; + for (var i in toString_props) { + if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[toString_props[i]]; + } + } + return str ? str : UNDEF_TYPE; + }; + } + if (!NAVIGATOR_UADATA) { + UAParserData.prototype.then = function (cb) { + cb(this); + return this; + }; + } + return new UAParserData(); + })(data); + } + UAParserItem.prototype.get = function (prop) { + if (!prop) return this.data; + return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; + }; + UAParserItem.prototype.parse = function () { + rgxMapper.call(this.data, this.ua, this.rgxMap); + return this; + }; + UAParserItem.prototype.set = function (prop, val) { + this.data[prop] = val; + return this; + }; + + function UAParserBrowser (ua, browserMap, uach) { + UAParserItem.call(this, [ + ua, + uach, + UA_BROWSER, + browserMap, + [NAME, VERSION, MAJOR], + [VERSION, MAJOR], + ' ?browser$', + [NAME, VERSION] + ]); + this.parse(); + // Brave-specific detection + if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); + } + this.set(MAJOR, majorize(this.get(VERSION))); + } + UAParserBrowser.prototype = new UAParserItem(); + UAParserBrowser.prototype.parseCH = function () { + var brands = this.uaCH[FULLVERLIST] || this.uaCH[BRANDS]; + if (brands) { + for (var i in brands) { + var brandName = brands[i].brand, + brandVersion = brands[i].version; + if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) { + this.set(NAME, strip(GOOGLE+' ', brandName)) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + } + } + return this; + }; + + function UAParserCPU (ua, cpuMap, uach) { + UAParserItem.call(this, [ + ua, + uach, + UA_CPU, + cpuMap, + [ARCHITECTURE], + [], + null, + [ARCHITECTURE] + ]); + this.parse(); + } + UAParserCPU.prototype = new UAParserItem(); + UAParserCPU.prototype.parseCH = function () { + var archName = this.uaCH[ARCHITECTURE]; + if (archName) { + archName += (archName && this.uaCH[BITNESS] == '64') ? '64' : EMPTY; + rgxMapper.call(this.data, archName, this.rgxMap); + } + return this; + }; + + function UAParserDevice (ua, deviceMap, uach) { + UAParserItem.call(this, [ + ua, + uach, + UA_DEVICE, + deviceMap, + [TYPE, MODEL, VENDOR], + [], + null, + [VENDOR, MODEL] + ]); + this.parse(); + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); + } + } + UAParserDevice.prototype = new UAParserItem(); + UAParserDevice.prototype.parseCH = function () { + if (this.uaCH[MOBILE]) { + this.set(TYPE, MOBILE); + } + if (this.uaCH[MODEL]) { + this.set(MODEL, this.uaCH[MODEL]); + } + return this; + }; + + function UAParserEngine (ua, engineMap) { + UAParserItem.call(this, [ + ua, + null, + UA_ENGINE, + engineMap, + [NAME, VERSION], + [VERSION], + null, + [NAME, VERSION] + ]); + this.parse(); + } + UAParserEngine.prototype = new UAParserItem(); + + function UAParserOS (ua, osMap, uach) { + UAParserItem.call(this, [ + ua, + uach, + UA_OS, + osMap, + [NAME, VERSION], + [VERSION], + ' ?os$', + [NAME, VERSION] + ]); + this.parse(); + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { + this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); + } + } + UAParserOS.prototype = new UAParserItem(); + UAParserOS.prototype.parseCH = function () { + var osName = this.uaCH[PLATFORM]; + if(osName) { + var osVersion = this.uaCH[PLATFORMVER]; + osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; + this.set(NAME, osName) + .set(VERSION, osVersion); + } + return this; + }; + + function UAParserResult (ua, resMap, uach) { + UAParserItem.call(this, [ua, uach, UA_RESULT, resMap]); + } + UAParserResult.prototype = new UAParserItem(); + + function UAParser (ua, extensions, headers) { + + if (typeof ua === OBJ_TYPE) { + if (isExtensions(ua)) { + if (typeof extensions === OBJ_TYPE) { + headers = extensions; // case UAParser(extensions, headers) + } + extensions = ua; // case UAParser(extensions) + } else { + headers = ua; // case UAParser(headers) + extensions = undefined; + } + ua = undefined; + } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { + headers = extensions; // case UAParser(ua, headers) + extensions = undefined; + } + + if (!(this instanceof UAParser)) { + return new UAParser(ua, extensions, headers).getResult(); + } + + var userAgent = ua || + ((NAVIGATOR && NAVIGATOR.userAgent) ? + NAVIGATOR.userAgent : + (headers && headers[USER_AGENT] ? + headers[USER_AGENT] : + EMPTY)), + + HTTP_UACH = new UAParserDataCH(headers, true), + + regexMap = extensions ? + extend(regexes, extensions) : + regexes; + + // public methods + this.getBrowser = function () { + return new UAParserBrowser(userAgent, regexMap[UA_BROWSER], HTTP_UACH).get(); + }; + + this.getCPU = function () { + return new UAParserCPU(userAgent, regexMap[UA_CPU], HTTP_UACH).get(); + }; + + this.getDevice = function () { + return new UAParserDevice(userAgent, regexMap[UA_DEVICE], HTTP_UACH).get(); + }; + + this.getEngine = function () { + return new UAParserEngine(userAgent, regexMap[UA_ENGINE]).get(); + }; + + this.getOS = function () { + return new UAParserOS(userAgent, regexMap[UA_OS], HTTP_UACH).get(); + }; + + this.getResult = function () { + return new UAParserResult(userAgent, regexMap, HTTP_UACH) + .set('ua', userAgent) + .set('ua_ch', HTTP_UACH) + .set(UA_BROWSER, this.getBrowser()) + .set(UA_CPU, this.getCPU()) + .set(UA_DEVICE, this.getDevice()) + .set(UA_ENGINE, this.getEngine()) + .set(UA_OS, this.getOS()) + .get(); + }; + + this.getUA = function () { + return userAgent; + }; + + this.setUA = function (ua) { + userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; + return this; + }; + + this.setUA(userAgent); + return this; + } + + UAParser.VERSION = LIBVERSION; + UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); + UAParser.CPU = enumerize([ARCHITECTURE]); + UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); + UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); + + export {UAParser}; \ No newline at end of file diff --git a/package.json b/package.json index 37854dce2..df2cc2d1b 100644 --- a/package.json +++ b/package.json @@ -139,14 +139,20 @@ "yuanyang ", "Yun Young-jin ", "Zach Bjornson " - ], + ], + "type": "commonjs", "main": "src/ua-parser.js", + "module": "dist/ua-parser.mjs", + "exports": { + "require": "./src/ua-parser.js", + "import": "./dist/ua-parser.mjs" + }, "files": [ "dist", "src" ], "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle", + "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('dist/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'const window = undefined;').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"", "test": "jshint src/ua-parser.js && mocha -R nyan test/test.js", "test-ci": "jshint src/ua-parser.js && mocha -R spec test/test.js", "verup": "node ./node_modules/verup", diff --git a/readme.md b/readme.md index 93f5c8159..2733a3005 100644 --- a/readme.md +++ b/readme.md @@ -473,6 +473,12 @@ http.createServer(function (req, res) { console.log('Server running at http://127.0.0.1:1337/'); ``` +## Using ES Modules + +```js +import { UAParser } from 'ua-parser-js'; +``` + ## Using TypeScript ```sh From df1a1c7bb4f6f8742db4a8185622411e6cd13ad4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 18 Mar 2023 18:50:42 +0700 Subject: [PATCH 044/388] Fix #624 - Detect Amazon Echo --- src/ua-parser.js | 4 +++- test/device-test.json | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 3f703533c..d5daf6e05 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -542,7 +542,7 @@ // Amazon /(alexa)webm/i, - /(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk + /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i, // Kindle Fire without Silk / Echo Show /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [ /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone @@ -727,6 +727,8 @@ /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla ], [VENDOR, [TYPE, EMBEDDED]], [ + /(aeobc)\b/i // Echo Dot + ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ //////////////////// // MIXED (GENERIC) diff --git a/test/device-test.json b/test/device-test.json index c40361f13..d23aba1e0 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -1638,6 +1638,33 @@ "type": "tablet" } }, + { + "desc": "Echo Show 5", + "ua": "Mozilla/5.0 (Linux; Android 5.1; AEORK Build/LVY48F; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEORK", + "type": "tablet" + } + }, + { + "desc": "Echo Show 8", + "ua": "Mozilla/5.0 (Linux; Android 7.1; AEOCH) AppleWebKit/537.36 (KHTML, like Gecko) Silk/77.2.21 like Chrome/77.0.3865.92 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOCH", + "type": "tablet" + } + }, + { + "desc": "Echo Dot", + "ua": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEOBC Build/LVY48F)", + "expect": { + "vendor": "Amazon", + "model": "AEOBC", + "type": "embedded" + } + }, { "desc": "Samsung Galaxy A21s", "ua": "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-A217F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.0 Chrome/75.0.3770.143 Mobile Safari/537.36", From 6b3fc3e0f10e191b24f6f66af0cfe18a1a5ea84f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 20 Mar 2023 01:17:04 +0700 Subject: [PATCH 045/388] Small refactor --- package.json | 2 +- src/ua-parser.js | 544 ++++++++++++++++++++-------------------------- test/es6-test.mjs | 17 ++ test/test.js | 2 + 4 files changed, 259 insertions(+), 306 deletions(-) create mode 100644 test/es6-test.mjs diff --git a/package.json b/package.json index df2cc2d1b..db710b2be 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ ], "scripts": { "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('dist/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'const window = undefined;').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"", - "test": "jshint src/ua-parser.js && mocha -R nyan test/test.js", + "test": "jshint src/ua-parser.js && mocha -R spec test/test.js && mocha -R spec test/es6-test.mjs", "test-ci": "jshint src/ua-parser.js && mocha -R spec test/test.js", "verup": "node ./node_modules/verup", "version": "node ./node_modules/verup 0" diff --git a/src/ua-parser.js b/src/ua-parser.js index 558dca77e..363482d1f 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -95,7 +95,18 @@ // Helper ////////// - var extend = function (regexes, extensions) { + var assignFromEntries = function (arr) { + for (var i in arr) { + var propName = arr[i]; + if (typeof propName == OBJ_TYPE && propName.length == 2) { + this[propName[0]] = propName[1]; + } else { + this[propName] = undefined; + } + } + return this; + }, + extend = function (regexes, extensions) { var mergedRegexes = {}; for (var i in regexes) { mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; @@ -110,18 +121,13 @@ return enums; }, has = function (str1, str2) { - return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; - }, - initialize = function (arr) { - for (var i in arr) { - var propName = arr[i]; - if (typeof propName == OBJ_TYPE && propName.length == 2) { - this[propName[0]] = propName[1]; - } else { - this[propName] = undefined; + if (typeof str1 === OBJ_TYPE && str1.length > 0) { + for (var i in str1) { + if (lowerize(str1[i]) == lowerize(str2)) return true; } + return false; } - return this; + return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, isExtensions = function (obj) { for (var prop in obj) { @@ -138,8 +144,8 @@ } return arr; }, - lowerize = function (str, rgx) { - return typeof(str) === STR_TYPE ? strip((rgx ? new RegExp(rgx, 'i') : EMPTY), str.toLowerCase()) : str; + lowerize = function (str) { + return typeof(str) === STR_TYPE ? str.toLowerCase() : str; }, majorize = function (version) { return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; @@ -270,7 +276,7 @@ // Regex map ///////////// - var regexes = { + var defaultRegexes = { browser : [[ @@ -845,15 +851,108 @@ ] }; + ///////////////// + // Factories + //////////////// + + var defaultProps = (function () { + var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; + assignFromEntries.call(props.init, [ + [UA_BROWSER, [NAME, VERSION, MAJOR]], + [UA_CPU, [ARCHITECTURE]], + [UA_DEVICE, [TYPE, MODEL, VENDOR]], + [UA_ENGINE, [NAME, VERSION]], + [UA_OS, [NAME, VERSION]] + ]); + assignFromEntries.call(props.isIgnore, [ + [UA_BROWSER, [VERSION, MAJOR]], + [UA_ENGINE, [VERSION]], + [UA_OS, [VERSION]] + ]); + assignFromEntries.call(props.isIgnoreRgx, [ + [UA_BROWSER, / ?browser$/i], + [UA_OS, / ?os$/i] + ]); + assignFromEntries.call(props.toString, [ + [UA_BROWSER, [NAME, VERSION]], + [UA_CPU, [ARCHITECTURE]], + [UA_DEVICE, [VENDOR, MODEL]], + [UA_ENGINE, [NAME, VERSION]], + [UA_OS, [NAME, VERSION]] + ]); + return props; + })(); + + var createUAParserData = function (itemType, ua, rgxMap, uaCH) { + + var init_props = defaultProps.init[itemType], + is_ignoreProps = defaultProps.isIgnore[itemType] || 0, + is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0, + toString_props = defaultProps.toString[itemType] || 0; + + function UAParserData () { + assignFromEntries.call(this, init_props); + } + UAParserData.prototype.withClientHints = function () { + + // nodejs / non-client-hints browsers + if (!NAVIGATOR_UADATA) { + return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get(); + } + + // browsers based on chromium 85+ + return NAVIGATOR_UADATA + .getHighEntropyValues(CH_ALL_VALUES) + .then(function (res) { + var JS_UACH = new UAParserDataCH(res, false); + return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get(); + }); + }; + + if (itemType != UA_RESULT) { + UAParserData.prototype.is = function (strToCheck) { + var is = false; + for (var i in this) { + if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; + } + } + return is; + }; + UAParserData.prototype.toString = function () { + var str = EMPTY; + for (var i in toString_props) { + if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[toString_props[i]]; + } + } + return str || UNDEF_TYPE; + }; + } + + if (!NAVIGATOR_UADATA) { + UAParserData.prototype.then = function (cb) { + cb(this); + return this; + }; + } + + return new UAParserData(); + }; + ///////////////// // Constructor //////////////// function UAParserDataCH (uach, isHTTP_UACH) { uach = uach || {}; - initialize.call(this, CH_ALL_VALUES); + assignFromEntries.call(this, CH_ALL_VALUES); if (isHTTP_UACH) { - initialize.call(this, [ + assignFromEntries.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], [BRANDS, itemListToArray(uach[CH_HEADER])], @@ -872,268 +971,126 @@ return this; } - function UAParserItem (data) { - if (!data) return; - this.ua = data[0]; - this.uaCH = data[1]; - this.rgxMap = data[3]; - this.data = (function (data) { - var ua = data[0], - uaCH = data[1], - itemType = data[2], - rgxMap = data[3], - init_props = data[4], - is_ignoreProps = data[5], - is_ignoreRgx = data[6], - toString_props = data[7]; - - function UAParserData () { - initialize.call(this, init_props); - } - UAParserData.prototype.withClientHints = function () { - - // nodejs / non-client-hints browsers - if (!NAVIGATOR_UADATA) { - - var HTTP_UACH = uaCH; - switch (itemType) { - case UA_BROWSER: - return new UAParserBrowser(ua, rgxMap, HTTP_UACH).parseCH().get(); - case UA_CPU: - return new UAParserCPU(ua, rgxMap, HTTP_UACH).parseCH().get(); - case UA_DEVICE: - return new UAParserDevice(ua, rgxMap, HTTP_UACH).parseCH().get(); - case UA_ENGINE: - return new UAParserEngine(ua, rgxMap).get(); - case UA_OS: - return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get(); - default : - return new UAParserResult(ua, rgxMap, HTTP_UACH) - .set('ua', ua) - .set('ua_ch', uaCH) - .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get()) - .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get()) - .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get()) - .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get()) - .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get()) - .get(); - } + function UAParserItem (itemType, ua, rgxMap, uaCH) { + assignFromEntries.call(this, [ + ['itemType', itemType], + ['ua', ua], + ['uaCH', uaCH], + ['rgxMap', rgxMap], + ['data', createUAParserData(itemType, ua, rgxMap, uaCH)] + ]); + this.parse(); + switch(this.itemType) { + case UA_BROWSER: + // Brave-specific detection + if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); } - - // browsers based on chromium 85+ - return NAVIGATOR_UADATA - .getHighEntropyValues(CH_ALL_VALUES) - .then(function (res) { - - var JS_UACH = new UAParserDataCH(res, false); - switch (itemType) { - case UA_BROWSER: - return UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get(); - case UA_CPU: - return new UAParserCPU(ua, rgxMap, JS_UACH).parseCH().get(); - case UA_DEVICE: - return new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get(); - case UA_ENGINE: - return new UAParserEngine(ua, rgxMap).get(); - case UA_OS: - return new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get(); - default : - return new UAParserResult(ua, rgxMap, JS_UACH) - .set('ua', ua) - .set('ua_ch', JS_UACH) - .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], JS_UACH).parseCH().get()) - .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], JS_UACH).parseCH().get()) - .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], JS_UACH).parseCH().get()) - .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get()) - .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], JS_UACH).parseCH().get()) - .get(); - } - }); - }; - if(itemType != UA_RESULT) { - UAParserData.prototype.is = function (strToCheck) { - var is = false; - for (var i in this) { - if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { - is = true; - if (strToCheck != UNDEF_TYPE) break; - } else if (strToCheck == UNDEF_TYPE && is) { - is = !is; - break; - } - } - return is; - }; - UAParserData.prototype.toString = function () { - var str = EMPTY; - for (var i in toString_props) { - if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { - str += (str ? ' ' : EMPTY) + this[toString_props[i]]; - } - } - return str ? str : UNDEF_TYPE; - }; - } - if (!NAVIGATOR_UADATA) { - UAParserData.prototype.then = function (cb) { - cb(this); - return this; + this.set(MAJOR, majorize(this.get(VERSION))); + break; + case UA_DEVICE: + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); + } + break; + case UA_OS: + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { + this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); + } + break; + case UA_RESULT: + var createUAParserItem = function (itemType) { + return new UAParserItem(itemType, ua, rgxMap, uaCH).get(); }; - } - return new UAParserData(); - })(data); + this.set('ua', ua) + .set('ua_ch', uaCH) + .set(UA_BROWSER, createUAParserItem(UA_BROWSER)) + .set(UA_CPU, createUAParserItem(UA_CPU)) + .set(UA_DEVICE, createUAParserItem(UA_DEVICE)) + .set(UA_ENGINE, createUAParserItem(UA_ENGINE)) + .set(UA_OS, createUAParserItem(UA_OS)) + .get(); + } + return this; } UAParserItem.prototype.get = function (prop) { if (!prop) return this.data; return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; }; UAParserItem.prototype.parse = function () { - rgxMapper.call(this.data, this.ua, this.rgxMap); - return this; - }; - UAParserItem.prototype.set = function (prop, val) { - this.data[prop] = val; - return this; - }; - - function UAParserBrowser (ua, browserMap, uach) { - UAParserItem.call(this, [ - ua, - uach, - UA_BROWSER, - browserMap, - [NAME, VERSION, MAJOR], - [VERSION, MAJOR], - ' ?browser$', - [NAME, VERSION] - ]); - this.parse(); - // Brave-specific detection - if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { - this.set(NAME, 'Brave'); - } - this.set(MAJOR, majorize(this.get(VERSION))); - } - UAParserBrowser.prototype = new UAParserItem(); - UAParserBrowser.prototype.parseCH = function () { - var brands = this.uaCH[FULLVERLIST] || this.uaCH[BRANDS]; - if (brands) { - for (var i in brands) { - var brandName = brands[i].brand, - brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) { - this.set(NAME, strip(GOOGLE+' ', brandName)) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - } - } + if (this.itemType != UA_RESULT) { + rgxMapper.call(this.data, this.ua, this.rgxMap[this.itemType]); } return this; }; - - function UAParserCPU (ua, cpuMap, uach) { - UAParserItem.call(this, [ - ua, - uach, - UA_CPU, - cpuMap, - [ARCHITECTURE], - [], - null, - [ARCHITECTURE] - ]); - this.parse(); - } - UAParserCPU.prototype = new UAParserItem(); - UAParserCPU.prototype.parseCH = function () { - var archName = this.uaCH[ARCHITECTURE]; - if (archName) { - archName += (archName && this.uaCH[BITNESS] == '64') ? '64' : EMPTY; - rgxMapper.call(this.data, archName, this.rgxMap); - } - return this; - }; - - function UAParserDevice (ua, deviceMap, uach) { - UAParserItem.call(this, [ - ua, - uach, - UA_DEVICE, - deviceMap, - [TYPE, MODEL, VENDOR], - [], - null, - [VENDOR, MODEL] - ]); - this.parse(); - if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { - this.set(TYPE, MOBILE); - } - // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { - this.set(MODEL, 'iPad') - .set(TYPE, TABLET); - } - } - UAParserDevice.prototype = new UAParserItem(); - UAParserDevice.prototype.parseCH = function () { - if (this.uaCH[MOBILE]) { - this.set(TYPE, MOBILE); - } - if (this.uaCH[MODEL]) { - this.set(MODEL, this.uaCH[MODEL]); + UAParserItem.prototype.parseCH = function () { + var ua = this.ua, + uaCH = this.uaCH, + rgxMap = this.rgxMap; + + switch (this.itemType) { + case UA_BROWSER: + var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; + if (brands) { + for (var i in brands) { + var brandName = brands[i].brand, + brandVersion = brands[i].version; + if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) { + this.set(NAME, strip(GOOGLE+' ', brandName)) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + } + } + break; + case UA_CPU: + var archName = uaCH[ARCHITECTURE]; + if (archName) { + archName += (archName && uaCH[BITNESS] == '64') ? '64' : EMPTY; + rgxMapper.call(this.data, archName, rgxMap[this.itemType]); + } + break; + case UA_DEVICE: + if (uaCH[MOBILE]) { + this.set(TYPE, MOBILE); + } + if (uaCH[MODEL]) { + this.set(MODEL, uaCH[MODEL]); + } + break; + case UA_OS: + var osName = uaCH[PLATFORM]; + if(osName) { + var osVersion = uaCH[PLATFORMVER]; + osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; + this.set(NAME, osName) + .set(VERSION, osVersion); + } + break; + case UA_RESULT: + var createUAParserItemWithCH = function (itemType) { + return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get(); + }; + this.set('ua', ua) + .set('ua_ch', uaCH) + .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER)) + .set(UA_CPU, createUAParserItemWithCH(UA_CPU)) + .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE)) + .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE)) + .set(UA_OS, createUAParserItemWithCH(UA_OS)); } return this; }; - - function UAParserEngine (ua, engineMap) { - UAParserItem.call(this, [ - ua, - null, - UA_ENGINE, - engineMap, - [NAME, VERSION], - [VERSION], - null, - [NAME, VERSION] - ]); - this.parse(); - } - UAParserEngine.prototype = new UAParserItem(); - - function UAParserOS (ua, osMap, uach) { - UAParserItem.call(this, [ - ua, - uach, - UA_OS, - osMap, - [NAME, VERSION], - [VERSION], - ' ?os$', - [NAME, VERSION] - ]); - this.parse(); - if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { - this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); - } - } - UAParserOS.prototype = new UAParserItem(); - UAParserOS.prototype.parseCH = function () { - var osName = this.uaCH[PLATFORM]; - if(osName) { - var osVersion = this.uaCH[PLATFORMVER]; - osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; - this.set(NAME, osName) - .set(VERSION, osVersion); - } + UAParserItem.prototype.set = function (prop, val) { + this.data[prop] = val; return this; }; - function UAParserResult (ua, resMap, uach) { - UAParserItem.call(this, [ua, uach, UA_RESULT, resMap]); - } - UAParserResult.prototype = new UAParserItem(); - function UAParser (ua, extensions, headers) { if (typeof ua === OBJ_TYPE) { @@ -1166,52 +1123,29 @@ HTTP_UACH = new UAParserDataCH(headers, true), regexMap = extensions ? - extend(regexes, extensions) : - regexes; - - // public methods - this.getBrowser = function () { - return new UAParserBrowser(userAgent, regexMap[UA_BROWSER], HTTP_UACH).get(); - }; - - this.getCPU = function () { - return new UAParserCPU(userAgent, regexMap[UA_CPU], HTTP_UACH).get(); - }; + extend(defaultRegexes, extensions) : + defaultRegexes, - this.getDevice = function () { - return new UAParserDevice(userAgent, regexMap[UA_DEVICE], HTTP_UACH).get(); - }; - - this.getEngine = function () { - return new UAParserEngine(userAgent, regexMap[UA_ENGINE]).get(); - }; - - this.getOS = function () { - return new UAParserOS(userAgent, regexMap[UA_OS], HTTP_UACH).get(); - }; - - this.getResult = function () { - return new UAParserResult(userAgent, regexMap, HTTP_UACH) - .set('ua', userAgent) - .set('ua_ch', HTTP_UACH) - .set(UA_BROWSER, this.getBrowser()) - .set(UA_CPU, this.getCPU()) - .set(UA_DEVICE, this.getDevice()) - .set(UA_ENGINE, this.getEngine()) - .set(UA_OS, this.getOS()) - .get(); - }; - - this.getUA = function () { - return userAgent; - }; - - this.setUA = function (ua) { - userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; - return this; - }; + createUAParserItemFunc = function (itemType) { + return function () { + return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH).get(); + }; + }; - this.setUA(userAgent); + // public methods + assignFromEntries.call(this, [ + ['getBrowser', createUAParserItemFunc(UA_BROWSER)], + ['getCPU', createUAParserItemFunc(UA_CPU)], + ['getDevice', createUAParserItemFunc(UA_DEVICE)], + ['getEngine', createUAParserItemFunc(UA_ENGINE)], + ['getOS', createUAParserItemFunc(UA_OS)], + ['getResult', createUAParserItemFunc(UA_RESULT)], + ['getUA', function () { return userAgent; }], + ['setUA', function (ua) { + userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; + return this; + }] + ]).setUA(userAgent); return this; } diff --git a/test/es6-test.mjs b/test/es6-test.mjs new file mode 100644 index 000000000..4d8495872 --- /dev/null +++ b/test/es6-test.mjs @@ -0,0 +1,17 @@ +import { UAParser } from '../dist/ua-parser.mjs' +import * as assert from 'assert' + +describe('Returns', () => { + it('getResult() should returns object', () => { + assert.deepEqual(new UAParser('').getResult(), + { + ua : '', + ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, + browser: { name: undefined, version: undefined, major: undefined }, + cpu: { architecture: undefined }, + device: { vendor: undefined, model: undefined, type: undefined }, + engine: { name: undefined, version: undefined}, + os: { name: undefined, version: undefined } + }); + }); +}); \ No newline at end of file diff --git a/test/test.js b/test/test.js index 39e948e1d..c744aa51f 100644 --- a/test/test.js +++ b/test/test.js @@ -195,6 +195,7 @@ describe('is() utility method', function () { assert.strictEqual(uap.getBrowser().name, "IEMobile"); assert.strictEqual(uap.getBrowser().is("IEMobile"), true); assert.strictEqual(uap.getBrowser().is("IE"), false); + assert.strictEqual(uap.getBrowser().is("11.0"), false); }); it('Should ignore "Browser" suffix', function () { @@ -204,6 +205,7 @@ describe('is() utility method', function () { it('Should ignore case', function () { assert.strictEqual(uap.getEngine().name, "Trident"); assert.strictEqual(uap.getEngine().is("tRiDeNt"), true); + assert.strictEqual(uap.getEngine().is("7.0"), false); }); it('Should get exact name', function () { From a1d816ae21e18c8debbe5f23657a0ce750290317 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 20 Mar 2023 23:36:51 +0700 Subject: [PATCH 046/388] Fix #519 #521 #616 - Improve iPhone & iOS detection --- src/ua-parser.js | 3 ++- test/device-test.json | 11 +++++++++++ test/os-test.json | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 363482d1f..02f10b244 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -462,7 +462,7 @@ ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ // Apple - /\((ip(?:hone|od)[\w ]*);/i // iPod/iPhone + /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [ /\((ipad);[-\w\),; ]+apple/i, // iPad /applecoremedia\/[\w\.]+ \((ipad)/i, @@ -789,6 +789,7 @@ // iOS/macOS /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS + /ios;fbsv\/([\d\.]+)/i, /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, diff --git a/test/device-test.json b/test/device-test.json index d23aba1e0..a7cb4b73f 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -3136,6 +3136,17 @@ "desc": "FaceBook Mobile App", "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", "expect": { + "vendor": "Apple", + "model": "iPhone12,1", + "type": "mobile" + } + }, + { + "desc": "Issue #519", + "ua": "ios/iPhone/14.2/SOME_CUSTOM_APP_VERSION", + "expect": { + "vendor": "Apple", + "model": "iPhone", "type": "mobile" } }, diff --git a/test/os-test.json b/test/os-test.json index f720a1aa6..b21274cbf 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -764,6 +764,15 @@ "version" : "undefined" } }, + { + "desc": "iOS with FaceBook Mobile App", + "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", + "expect": + { + "name" : "iOS", + "version" : "13.6.1" + } + }, { "desc" : "watchOS", "ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]", From 49d6422ebd173d90d642ba4717de77e0439291c6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 22 Mar 2023 10:53:15 +0700 Subject: [PATCH 047/388] Mistype in iPad detection --- src/ua-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 02f10b244..08ea12811 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -994,7 +994,7 @@ this.set(TYPE, MOBILE); } // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { this.set(MODEL, 'iPad') .set(TYPE, TABLET); } From 33df5dc698ee62034ae7fa1ffa4497d0280d0d56 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 22 Mar 2023 11:02:26 +0700 Subject: [PATCH 048/388] Fix #637 - Detect Safari on iPhone as Safari Mobile --- src/ua-parser.js | 2 ++ test/browser-test.json | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/ua-parser.js b/src/ua-parser.js index 08ea12811..ecf0fc345 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -382,6 +382,8 @@ /version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i // Safari Mobile ], [VERSION, [NAME, 'Safari '+SUFFIX_MOBILE]], [ + /iphone .*mobile(?:\/\w+ | ?)safari/i + ], [[NAME, 'Safari '+SUFFIX_MOBILE]], [ /version\/([\w\.\,]+) .*(safari)/i // Safari ], [VERSION, NAME], [ /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 diff --git a/test/browser-test.json b/test/browser-test.json index 20e7d54f3..3942c50ae 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -758,6 +758,16 @@ "major" : "4" } }, + { + "desc" : "Mobile Safari", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Safari/604.1", + "expect" : + { + "name" : "Safari Mobile", + "version" : "undefined", + "major" : "undefined" + } + }, { "desc" : "Mosaic", "ua" : "NCSA_Mosaic/2.6 (X11; SunOS 4.1.3 sun4m)", From 33f02099d1bf2e074446c0608c4fbfcd22371475 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 22 Mar 2023 11:18:52 +0700 Subject: [PATCH 049/388] Fix #678 - Improve Yandex detection --- src/ua-parser.js | 2 +- test/browser-test.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index ecf0fc345..6035a1ff6 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -319,7 +319,7 @@ ], [VERSION, [NAME, 'Konqueror']], [ /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11 ], [VERSION, [NAME, 'IE']], [ - /yabrowser\/([\w\.]+)/i // Yandex + /ya(?:search)?browser\/([\w\.]+)/i // Yandex ], [VERSION, [NAME, 'Yandex']], [ /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser ], [[NAME, /(.+)/, '$1 Secure '+SUFFIX_BROWSER], VERSION], [ diff --git a/test/browser-test.json b/test/browser-test.json index 3942c50ae..63a501a9b 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -1308,6 +1308,16 @@ "major" : "1" } }, + { + "desc" : "Yandex", + "ua" : "Mozilla/5.0 (Linux; arm_64; Android 11; M2101K7AG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.125 YaApp_Android/22.70 YaSearchBrowser/22.70 BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect" : + { + "name" : "Yandex", + "version" : "22.70", + "major" : "22" + } + }, { "desc" : "Puffin", "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo P2a42 Build/MMB29M; en-us) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Puffin/6.0.8.15804AP", From 172f57ffea929a663dc26b5facdf3bd035251004 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 22 Mar 2023 11:51:28 +0700 Subject: [PATCH 050/388] Fix #636 - Add new browser: HeyTap | Fix #248 - Add 'Mobile' prefix for mobile version of Chrome, Firefox, Safari --- readme.md | 6 +++--- src/ua-parser.js | 35 ++++++++++++++++++----------------- test/browser-test.json | 30 ++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/readme.md b/readme.md index 90e72730d..7ad187291 100644 --- a/readme.md +++ b/readme.md @@ -102,16 +102,16 @@ The methods are self explanatory, here's a small overview on all the available m # Possible 'browser.name': 2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG, BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera, -Chrome [Mobile/Headless/WebView], Chromium, Cobalt, Comodo Dragon, Dillo, +[Mobile] Chrome [Headless/WebView], Chromium, Cobalt, Comodo Dragon, Dillo, Dolphin, Doris, DuckDuckGo, Edge, Electron, Epiphany, Facebook, Falkon, Fennec, -Firebird, Firefox [Mobile/Focus/Reality], Flock, Flow, GSA, GoBrowser, +Firebird, [Mobile] Firefox [Focus/Reality], Flock, Flow, GSA, GoBrowser, HeyTap, Huawei Browser, ICE Browser, IE, IEMobile, IceApe, IceCat, IceDragon, Iceweasel, Instagram, Iridium, Iron, Jasmine, Kakao[Story/Talk], K-Meleon, Kindle, Klar, Konqueror, LBBROWSER, Line, LinkedIn, Links, Lunascape, Lynx, MIUI Browser, Maemo Browser, Maemo, Maxthon, MetaSr Midori, Minimo, Mosaic, Mozilla, NetFront, NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb, Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris, -Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, Safari [Mobile], +Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, [Mobile] Safari, Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim, SlimBrowser, Swiftfox, Tesla, Tizen Browser, UCBrowser, UP.Browser, Viera, Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ... diff --git a/src/ua-parser.js b/src/ua-parser.js index 6035a1ff6..931ef329d 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -75,8 +75,8 @@ XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', ZTE = 'ZTE', - SUFFIX_BROWSER = 'Browser', - SUFFIX_MOBILE = 'Mobile', + PREFIX_MOBILE = 'Mobile ', + SUFFIX_BROWSER = ' Browser', CHROME = 'Chrome', EDGE = 'Edge', FIREFOX = 'Firefox', @@ -280,8 +280,9 @@ browser : [[ + // Most common regardless engine /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS - ], [VERSION, [NAME, 'Chrome']], [ + ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge ], [VERSION, [NAME, 'Edge']], [ @@ -305,11 +306,12 @@ // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo + /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser - ], [VERSION, [NAME, 'UC'+SUFFIX_BROWSER]], [ + ], [VERSION, [NAME, 'UCBrowser']], [ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser /\bqbcore\/([\w\.]+).+microm/i ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [ @@ -322,7 +324,7 @@ /ya(?:search)?browser\/([\w\.]+)/i // Yandex ], [VERSION, [NAME, 'Yandex']], [ /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser - ], [[NAME, /(.+)/, '$1 Secure '+SUFFIX_BROWSER], VERSION], [ + ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [ /\bfocus\/([\w\.]+)/i // Firefox Focus ], [VERSION, [NAME, FIREFOX+' Focus']], [ /\bopt\/([\w\.]+)/i // Opera Touch @@ -334,13 +336,13 @@ /coast\/([\w\.]+)/i // Opera Coast ], [VERSION, [NAME, OPERA+' Coast']], [ /miuibrowser\/([\w\.]+)/i // MIUI Browser - ], [VERSION, [NAME, 'MIUI '+SUFFIX_BROWSER]], [ + ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [ /fxios\/([\w\.-]+)/i // Firefox for iOS - ], [VERSION, [NAME, 'Firefox '+SUFFIX_MOBILE]], [ + ], [VERSION, [NAME, PREFIX_MOBILE + 'Firefox']], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 - ], [[NAME, '360 '+SUFFIX_BROWSER]], [ + ], [[NAME, '360' + SUFFIX_BROWSER]], [ /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1 '+SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon ], [[NAME, /_/g, ' '], VERSION], [ /(electron)\/([\w\.]+) safari/i, // Electron-based App @@ -371,19 +373,18 @@ ], [[NAME, CHROME+' WebView'], VERSION], [ /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser - ], [VERSION, [NAME, 'Android '+SUFFIX_BROWSER]], [ + ], [VERSION, [NAME, 'Android' + SUFFIX_BROWSER]], [ - /chrome\/([\w\.]+) mobile/i, // Chrome Mobile - /(?:(?:android.+)crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS - ], [VERSION, [NAME, 'Chrome '+SUFFIX_MOBILE]], [ + /chrome\/([\w\.]+) mobile/i // Chrome Mobile + ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia ], [NAME, VERSION], [ /version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i // Safari Mobile - ], [VERSION, [NAME, 'Safari '+SUFFIX_MOBILE]], [ + ], [VERSION, [NAME, PREFIX_MOBILE + 'Safari']], [ /iphone .*mobile(?:\/\w+ | ?)safari/i - ], [[NAME, 'Safari '+SUFFIX_MOBILE]], [ + ], [[NAME, PREFIX_MOBILE + 'Safari']], [ /version\/([\w\.\,]+) .*(safari)/i // Safari ], [VERSION, NAME], [ /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 @@ -394,7 +395,7 @@ // Gecko based /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile - ], [[NAME, 'Firefox '+SUFFIX_MOBILE], VERSION], [ + ], [[NAME, PREFIX_MOBILE + 'Firefox'], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality diff --git a/test/browser-test.json b/test/browser-test.json index 63a501a9b..0b0345f22 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -234,7 +234,7 @@ "ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3", "expect" : { - "name" : "Chrome", + "name" : "Mobile Chrome", "version" : "19.0.1084.60", "major" : "19" } @@ -254,7 +254,7 @@ "ua" : "Mozilla/5.0 (Linux; U; Android-4.0.3; en-us; Galaxy Nexus Build/IML74K) AppleWebKit/535.7 (KHTML, like Gecko) CrMo/16.0.912.75 Mobile Safari/535.7", "expect" : { - "name" : "Chrome", + "name" : "Mobile Chrome", "version" : "16.0.912.75", "major" : "16" } @@ -508,6 +508,16 @@ "major" : "1" } }, + { + "desc" : "HeyTap", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.61 Safari/537.36 HeyTapBrowser/40.8.10.1", + "expect" : + { + "name" : "HeyTap", + "version" : "40.8.10.1", + "major" : "40" + } + }, { "desc" : "HuaweiBrowser", "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; LYA-AL00;HMSCore/4.0.0 GMS/10.4 ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.64 HuaweiBrowser/10.0.3.102 Mobile Safari/537.36", @@ -753,7 +763,7 @@ "ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7", "expect" : { - "name" : "Safari Mobile", + "name" : "Mobile Safari", "version" : "4.0.5", "major" : "4" } @@ -763,7 +773,7 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Safari/604.1", "expect" : { - "name" : "Safari Mobile", + "name" : "Mobile Safari", "version" : "undefined", "major" : "undefined" } @@ -1413,7 +1423,7 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) FxiOS/1.1 Mobile/13B143 Safari/601.1.46", "expect" : { - "name" : "Firefox Mobile", + "name" : "Mobile Firefox", "version" : "1.1", "major" : "1" } @@ -1423,7 +1433,7 @@ "ua" : "Mozilla/5.0 (iPad; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4", "expect" : { - "name" : "Firefox Mobile", + "name" : "Mobile Firefox", "version" : "1.0", "major" : "1" } @@ -1617,7 +1627,7 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 15_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6,2 Mobile/15E148 Safari/604.1", "expect" : { - "name" : "Safari Mobile", + "name" : "Mobile Safari", "version" : "15.6,2", "major" : "15" } @@ -1708,7 +1718,7 @@ "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36", "expect" : { - "name" : "Chrome Mobile", + "name" : "Mobile Chrome", "version" : "58.0.3029.83", "major" : "58" } @@ -1718,7 +1728,7 @@ "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) FxiOS/7.5b3349 Mobile/14F89 Safari/603.2.4", "expect" : { - "name" : "Firefox Mobile", + "name" : "Mobile Firefox", "version" : "7.5b3349", "major" : "7" } @@ -1728,7 +1738,7 @@ "ua" : "Mozilla/5.0 (Android 5.0; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0", "expect" : { - "name" : "Firefox Mobile", + "name" : "Mobile Firefox", "version" : "41.0", "major" : "41" } From 3af8e1e272a252d3b55ab8815dfafc8e3d8a9589 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 23 Mar 2023 21:43:19 +0700 Subject: [PATCH 051/388] Upon creating new item, only pass regex map thats relevant with its itemType --- src/ua-parser.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 931ef329d..04a43b613 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -969,7 +969,7 @@ ]); } else { for (var prop in uach) { - if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = stripQuotes(uach[prop]); + if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop]; } } return this; @@ -1009,7 +1009,7 @@ break; case UA_RESULT: var createUAParserItem = function (itemType) { - return new UAParserItem(itemType, ua, rgxMap, uaCH).get(); + return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get(); }; this.set('ua', ua) .set('ua_ch', uaCH) @@ -1028,7 +1028,7 @@ }; UAParserItem.prototype.parse = function () { if (this.itemType != UA_RESULT) { - rgxMapper.call(this.data, this.ua, this.rgxMap[this.itemType]); + rgxMapper.call(this.data, this.ua, this.rgxMap); } return this; }; @@ -1055,8 +1055,8 @@ case UA_CPU: var archName = uaCH[ARCHITECTURE]; if (archName) { - archName += (archName && uaCH[BITNESS] == '64') ? '64' : EMPTY; - rgxMapper.call(this.data, archName, rgxMap[this.itemType]); + if (archName && uaCH[BITNESS] == '64') archName += '64'; + rgxMapper.call(this.data, archName, rgxMap); } break; case UA_DEVICE: @@ -1071,14 +1071,14 @@ var osName = uaCH[PLATFORM]; if(osName) { var osVersion = uaCH[PLATFORMVER]; - osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; + if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10'); this.set(NAME, osName) .set(VERSION, osVersion); } break; case UA_RESULT: var createUAParserItemWithCH = function (itemType) { - return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get(); + return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get(); }; this.set('ua', ua) .set('ua_ch', uaCH) @@ -1132,7 +1132,7 @@ createUAParserItemFunc = function (itemType) { return function () { - return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH).get(); + return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get(); }; }; From e70d09a1f8f72ef41658a3e9f0a079f0c7ae4229 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 25 Mar 2023 07:21:17 +0700 Subject: [PATCH 052/388] Fix #489: ARM arch detection & create test that simulates HTTP headers sent from an Apple silicon --- src/ua-parser.js | 2 +- test/test.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 04a43b613..3fb1e122f 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -1056,7 +1056,7 @@ var archName = uaCH[ARCHITECTURE]; if (archName) { if (archName && uaCH[BITNESS] == '64') archName += '64'; - rgxMapper.call(this.data, archName, rgxMap); + rgxMapper.call(this.data, archName + ';', rgxMap); } break; case UA_DEVICE: diff --git a/test/test.js b/test/test.js index c744aa51f..8077e82da 100644 --- a/test/test.js +++ b/test/test.js @@ -483,6 +483,36 @@ describe('Map UA-CH headers', function () { assert.strictEqual(uap.os.name, "Linux"); assert.strictEqual(uap.os.version, "x86_64"); }); + + it('Can detect Apple silicon from client hints data', function () { + + // https://github.com/faisalman/ua-parser-js/issues/489#issuecomment-1479213579 + const httpHeadersFromAppleSilicon = { + 'sec-ch-ua-arch' : 'arm', + 'sec-ch-ua-platform' : 'macOS', + 'sec-ch-ua-mobile' : '?0', + 'sec-ch-ua' : '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', + 'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0' + }; + + UAParser(httpHeadersFromAppleSilicon).withClientHints().then(function (ua) { + + // Only works in Chrome + /* + if (ua.os.is("macOS") && + ua.cpu.is("arm") && + !ua.device.is("mobile") && + !ua.device.is("tablet")) { + // possibly an Apple silicon device + } + */ + + assert.strictEqual(ua.os.is("macOS"), true); + assert.strictEqual(ua.cpu.is("arm"), true); + assert.strictEqual(ua.device.is("mobile"), false); + assert.strictEqual(ua.device.is("tablet"), false); + }); + }); }); describe('Map UA-CH JS', () => { From 82567c28ab15227dc864f35f113c6df8a1ec40b8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 26 Mar 2023 11:57:17 +0700 Subject: [PATCH 053/388] Fix #639: Only check for Brave properties if given userAgent match the current userAgent --- src/ua-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 3fb1e122f..bdf703603 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -987,7 +987,7 @@ switch(this.itemType) { case UA_BROWSER: // Brave-specific detection - if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + if (NAVIGATOR && NAVIGATOR.userAgent == ua && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { this.set(NAME, 'Brave'); } this.set(MAJOR, majorize(this.get(VERSION))); From ba28d33d51c00bd39197821aec93c41acf1ef922 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 27 Mar 2023 22:25:17 +0700 Subject: [PATCH 054/388] Move generated .mjs file to /src --- package.json | 10 +- {dist => src}/ua-parser.mjs | 646 +++++++++++++++++------------------- test/es6-test.mjs | 2 +- 3 files changed, 302 insertions(+), 356 deletions(-) rename {dist => src}/ua-parser.mjs (77%) diff --git a/package.json b/package.json index db710b2be..b00b5c5c8 100644 --- a/package.json +++ b/package.json @@ -142,19 +142,19 @@ ], "type": "commonjs", "main": "src/ua-parser.js", - "module": "dist/ua-parser.mjs", + "module": "src/ua-parser.mjs", "exports": { "require": "./src/ua-parser.js", - "import": "./dist/ua-parser.mjs" + "import": "./src/ua-parser.mjs" }, "files": [ "dist", "src" ], "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('dist/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'const window = undefined;').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"", - "test": "jshint src/ua-parser.js && mocha -R spec test/test.js && mocha -R spec test/es6-test.mjs", - "test-ci": "jshint src/ua-parser.js && mocha -R spec test/test.js", + "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"", + "test": "jshint src/ua-parser.js && mocha -R nyan test", + "test-ci": "jshint src/ua-parser.js && mocha -R spec test", "verup": "node ./node_modules/verup", "version": "node ./node_modules/verup 0" }, diff --git a/dist/ua-parser.mjs b/src/ua-parser.mjs similarity index 77% rename from dist/ua-parser.mjs rename to src/ua-parser.mjs index 7d3aafd1e..1821d97dc 100644 --- a/dist/ua-parser.mjs +++ b/src/ua-parser.mjs @@ -1,6 +1,8 @@ // Generated ESM version of UAParser.js // Source file: /src/ua-parser.js +const window = undefined; + ///////////////////////////////////////////////////////////////////////////////// /* UAParser.js v1.1.34 Copyright © 2012-2023 Faisal Salman @@ -11,7 +13,7 @@ Source : https://github.com/faisalman/ua-parser-js */ ///////////////////////////////////////////////////////////////////////////////// -const window = undefined; + ////////////// // Constants @@ -53,39 +55,37 @@ const window = undefined; CH_HEADER_MODEL = CH_HEADER + '-model', CH_HEADER_PLATFORM = CH_HEADER + '-platform', CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'], + CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', UA_ENGINE = 'engine', UA_OS = 'os', - UA_RESULT = 'result'; - - var AMAZON = 'Amazon', - APPLE = 'Apple', - ASUS = 'ASUS', - BLACKBERRY = 'BlackBerry', - GOOGLE = 'Google', - HUAWEI = 'Huawei', - LG = 'LG', - MICROSOFT = 'Microsoft', - MOTOROLA = 'Motorola', - SAMSUNG = 'Samsung', - SHARP = 'Sharp', - SONY = 'Sony', - SWISS = 'Swiss', - XIAOMI = 'Xiaomi', - ZEBRA = 'Zebra', - ZTE = 'ZTE', - - BROWSER = 'Browser', - CHROME = 'Chrome', - EDGE = 'Edge', - FIREFOX = 'Firefox', - OPERA = 'Opera', + UA_RESULT = 'result', + AMAZON = 'Amazon', + APPLE = 'Apple', + ASUS = 'ASUS', + BLACKBERRY = 'BlackBerry', + GOOGLE = 'Google', + HUAWEI = 'Huawei', + LG = 'LG', + MICROSOFT = 'Microsoft', + MOTOROLA = 'Motorola', + SAMSUNG = 'Samsung', + SHARP = 'Sharp', + SONY = 'Sony', + SWISS = 'Swiss', + XIAOMI = 'Xiaomi', + ZEBRA = 'Zebra', + ZTE = 'ZTE', + PREFIX_MOBILE = 'Mobile ', + SUFFIX_BROWSER = ' Browser', + CHROME = 'Chrome', + EDGE = 'Edge', + FIREFOX = 'Firefox', + OPERA = 'Opera', FACEBOOK = 'Facebook', - - WINDOWS = 'Windows'; + WINDOWS = 'Windows'; var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : @@ -98,7 +98,18 @@ const window = undefined; // Helper ////////// - var extend = function (regexes, extensions) { + var assignFromEntries = function (arr) { + for (var i in arr) { + var propName = arr[i]; + if (typeof propName == OBJ_TYPE && propName.length == 2) { + this[propName[0]] = propName[1]; + } else { + this[propName] = undefined; + } + } + return this; + }, + extend = function (regexes, extensions) { var mergedRegexes = {}; for (var i in regexes) { mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; @@ -113,18 +124,13 @@ const window = undefined; return enums; }, has = function (str1, str2) { - return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; - }, - initialize = function (arr) { - for (var i in arr) { - var propName = arr[i]; - if (typeof propName == OBJ_TYPE && propName.length == 2) { - this[propName[0]] = propName[1]; - } else { - this[propName] = undefined; + if (typeof str1 === OBJ_TYPE && str1.length > 0) { + for (var i in str1) { + if (lowerize(str1[i]) == lowerize(str2)) return true; } + return false; } - return this; + return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, isExtensions = function (obj) { for (var prop in obj) { @@ -141,8 +147,8 @@ const window = undefined; } return arr; }, - lowerize = function (str, rgx) { - return typeof(str) === STR_TYPE ? strip((rgx ? new RegExp(rgx, 'i') : EMPTY), str.toLowerCase()) : str; + lowerize = function (str) { + return typeof(str) === STR_TYPE ? str.toLowerCase() : str; }, majorize = function (version) { return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; @@ -273,12 +279,13 @@ const window = undefined; // Regex map ///////////// - var regexes = { + var defaultRegexes = { browser : [[ + // Most common regardless engine /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS - ], [VERSION, [NAME, 'Chrome']], [ + ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge ], [VERSION, [NAME, 'Edge']], [ @@ -302,11 +309,12 @@ const window = undefined; // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo + /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser - ], [VERSION, [NAME, 'UC'+BROWSER]], [ + ], [VERSION, [NAME, 'UCBrowser']], [ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser /\bqbcore\/([\w\.]+).+microm/i ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [ @@ -316,10 +324,10 @@ const window = undefined; ], [VERSION, [NAME, 'Konqueror']], [ /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11 ], [VERSION, [NAME, 'IE']], [ - /yabrowser\/([\w\.]+)/i // Yandex + /ya(?:search)?browser\/([\w\.]+)/i // Yandex ], [VERSION, [NAME, 'Yandex']], [ /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser - ], [[NAME, /(.+)/, '$1 Secure '+BROWSER], VERSION], [ + ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [ /\bfocus\/([\w\.]+)/i // Firefox Focus ], [VERSION, [NAME, FIREFOX+' Focus']], [ /\bopt\/([\w\.]+)/i // Opera Touch @@ -331,13 +339,13 @@ const window = undefined; /coast\/([\w\.]+)/i // Opera Coast ], [VERSION, [NAME, OPERA+' Coast']], [ /miuibrowser\/([\w\.]+)/i // MIUI Browser - ], [VERSION, [NAME, 'MIUI '+BROWSER]], [ - /fxios\/([-\w\.]+)/i // Firefox for iOS - ], [VERSION, [NAME, FIREFOX]], [ + ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [ + /fxios\/([\w\.-]+)/i // Firefox for iOS + ], [VERSION, [NAME, PREFIX_MOBILE + 'Firefox']], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 - ], [[NAME, '360 '+BROWSER]], [ + ], [[NAME, '360' + SUFFIX_BROWSER]], [ /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1 '+BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon ], [[NAME, /_/g, ' '], VERSION], [ /(electron)\/([\w\.]+) safari/i, // Electron-based App @@ -368,14 +376,19 @@ const window = undefined; ], [[NAME, CHROME+' WebView'], VERSION], [ /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser - ], [VERSION, [NAME, 'Android '+BROWSER]], [ + ], [VERSION, [NAME, 'Android' + SUFFIX_BROWSER]], [ + + /chrome\/([\w\.]+) mobile/i // Chrome Mobile + ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia ], [NAME, VERSION], [ - /version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i // Mobile Safari - ], [VERSION, [NAME, 'Mobile Safari']], [ - /version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i // Safari & Safari Mobile + /version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i // Safari Mobile + ], [VERSION, [NAME, PREFIX_MOBILE + 'Safari']], [ + /iphone .*mobile(?:\/\w+ | ?)safari/i + ], [[NAME, PREFIX_MOBILE + 'Safari']], [ + /version\/([\w\.\,]+) .*(safari)/i // Safari ], [VERSION, NAME], [ /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 ], [NAME, [VERSION, strMapper, oldSafariMap]], [ @@ -384,6 +397,8 @@ const window = undefined; ], [NAME, VERSION], [ // Gecko based + /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile + ], [[NAME, PREFIX_MOBILE + 'Firefox'], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality @@ -453,7 +468,7 @@ const window = undefined; ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ // Apple - /\((ip(?:hone|od)[\w ]*);/i // iPod/iPhone + /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [ /\((ipad);[-\w\),; ]+apple/i, // iPad /applecoremedia\/[\w\.]+ \((ipad)/i, @@ -543,7 +558,7 @@ const window = undefined; // Amazon /(alexa)webm/i, - /(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk + /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i, // Kindle Fire without Silk / Echo Show /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [ /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone @@ -728,6 +743,8 @@ const window = undefined; /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla ], [VENDOR, [TYPE, EMBEDDED]], [ + /(aeobc)\b/i // Echo Dot + ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ //////////////////// // MIXED (GENERIC) @@ -778,6 +795,7 @@ const window = undefined; // iOS/macOS /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS + /ios;fbsv\/([\d\.]+)/i, /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, @@ -840,15 +858,108 @@ const window = undefined; ] }; + ///////////////// + // Factories + //////////////// + + var defaultProps = (function () { + var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; + assignFromEntries.call(props.init, [ + [UA_BROWSER, [NAME, VERSION, MAJOR]], + [UA_CPU, [ARCHITECTURE]], + [UA_DEVICE, [TYPE, MODEL, VENDOR]], + [UA_ENGINE, [NAME, VERSION]], + [UA_OS, [NAME, VERSION]] + ]); + assignFromEntries.call(props.isIgnore, [ + [UA_BROWSER, [VERSION, MAJOR]], + [UA_ENGINE, [VERSION]], + [UA_OS, [VERSION]] + ]); + assignFromEntries.call(props.isIgnoreRgx, [ + [UA_BROWSER, / ?browser$/i], + [UA_OS, / ?os$/i] + ]); + assignFromEntries.call(props.toString, [ + [UA_BROWSER, [NAME, VERSION]], + [UA_CPU, [ARCHITECTURE]], + [UA_DEVICE, [VENDOR, MODEL]], + [UA_ENGINE, [NAME, VERSION]], + [UA_OS, [NAME, VERSION]] + ]); + return props; + })(); + + var createUAParserData = function (itemType, ua, rgxMap, uaCH) { + + var init_props = defaultProps.init[itemType], + is_ignoreProps = defaultProps.isIgnore[itemType] || 0, + is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0, + toString_props = defaultProps.toString[itemType] || 0; + + function UAParserData () { + assignFromEntries.call(this, init_props); + } + UAParserData.prototype.withClientHints = function () { + + // nodejs / non-client-hints browsers + if (!NAVIGATOR_UADATA) { + return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get(); + } + + // browsers based on chromium 85+ + return NAVIGATOR_UADATA + .getHighEntropyValues(CH_ALL_VALUES) + .then(function (res) { + var JS_UACH = new UAParserDataCH(res, false); + return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get(); + }); + }; + + if (itemType != UA_RESULT) { + UAParserData.prototype.is = function (strToCheck) { + var is = false; + for (var i in this) { + if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; + } + } + return is; + }; + UAParserData.prototype.toString = function () { + var str = EMPTY; + for (var i in toString_props) { + if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[toString_props[i]]; + } + } + return str || UNDEF_TYPE; + }; + } + + if (!NAVIGATOR_UADATA) { + UAParserData.prototype.then = function (cb) { + cb(this); + return this; + }; + } + + return new UAParserData(); + }; + ///////////////// // Constructor //////////////// function UAParserDataCH (uach, isHTTP_UACH) { uach = uach || {}; - initialize.call(this, CH_ALL_VALUES); + assignFromEntries.call(this, CH_ALL_VALUES); if (isHTTP_UACH) { - initialize.call(this, [ + assignFromEntries.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], [BRANDS, itemListToArray(uach[CH_HEADER])], @@ -861,274 +972,132 @@ const window = undefined; ]); } else { for (var prop in uach) { - if(this.hasOwnProperty(prop) && uach[prop]) this[prop] = stripQuotes(uach[prop]); + if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop]; } } return this; } - function UAParserItem (data) { - if (!data) return; - this.ua = data[0]; - this.uaCH = data[1]; - this.rgxMap = data[3]; - this.data = (function (data) { - var ua = data[0], - uaCH = data[1], - itemType = data[2], - rgxMap = data[3], - init_props = data[4], - is_ignoreProps = data[5], - is_ignoreRgx = data[6], - toString_props = data[7]; - - function UAParserData () { - initialize.call(this, init_props); - } - UAParserData.prototype.withClientHints = function () { - - // nodejs / non-client-hints browsers - if (!NAVIGATOR_UADATA) { - - var HTTP_UACH = uaCH; - switch (itemType) { - case UA_BROWSER: - return new UAParserBrowser(ua, rgxMap, HTTP_UACH).parseCH().get(); - case UA_CPU: - return new UAParserCPU(ua, rgxMap, HTTP_UACH).parseCH().get(); - case UA_DEVICE: - return new UAParserDevice(ua, rgxMap, HTTP_UACH).parseCH().get(); - case UA_ENGINE: - return new UAParserEngine(ua, rgxMap).get(); - case UA_OS: - return new UAParserOS(ua, rgxMap, HTTP_UACH).parseCH().get(); - default : - return new UAParserResult(ua, rgxMap, HTTP_UACH) - .set('ua', ua) - .set('ua_ch', uaCH) - .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], HTTP_UACH).parseCH().get()) - .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], HTTP_UACH).parseCH().get()) - .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], HTTP_UACH).parseCH().get()) - .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get()) - .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], HTTP_UACH).parseCH().get()) - .get(); - } + function UAParserItem (itemType, ua, rgxMap, uaCH) { + assignFromEntries.call(this, [ + ['itemType', itemType], + ['ua', ua], + ['uaCH', uaCH], + ['rgxMap', rgxMap], + ['data', createUAParserData(itemType, ua, rgxMap, uaCH)] + ]); + this.parse(); + switch(this.itemType) { + case UA_BROWSER: + // Brave-specific detection + if (NAVIGATOR && NAVIGATOR.userAgent == ua && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); } - - // browsers based on chromium 85+ - return NAVIGATOR_UADATA - .getHighEntropyValues(CH_ALL_VALUES) - .then(function (res) { - - var JS_UACH = new UAParserDataCH(res, false); - switch (itemType) { - case UA_BROWSER: - return UAParserBrowser(ua, rgxMap, JS_UACH).parseCH().get(); - case UA_CPU: - return new UAParserCPU(ua, rgxMap, JS_UACH).parseCH().get(); - case UA_DEVICE: - return new UAParserDevice(ua, rgxMap, JS_UACH).parseCH().get(); - case UA_ENGINE: - return new UAParserEngine(ua, rgxMap).get(); - case UA_OS: - return new UAParserOS(ua, rgxMap, JS_UACH).parseCH().get(); - default : - return new UAParserResult(ua, rgxMap, JS_UACH) - .set('ua', ua) - .set('ua_ch', JS_UACH) - .set(UA_BROWSER, new UAParserBrowser(ua, rgxMap[UA_BROWSER], JS_UACH).parseCH().get()) - .set(UA_CPU, new UAParserCPU(ua, rgxMap[UA_CPU], JS_UACH).parseCH().get()) - .set(UA_DEVICE, new UAParserDevice(ua, rgxMap[UA_DEVICE], JS_UACH).parseCH().get()) - .set(UA_ENGINE, new UAParserEngine(ua, rgxMap[UA_ENGINE]).get()) - .set(UA_OS, new UAParserOS(ua, rgxMap[UA_OS], JS_UACH).parseCH().get()) - .get(); - } - }); - }; - if(itemType != UA_RESULT) { - UAParserData.prototype.is = function (strToCheck) { - var is = false; - for (var i in this) { - if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) { - is = true; - if (strToCheck != UNDEF_TYPE) break; - } else if (strToCheck == UNDEF_TYPE && is) { - is = !is; - break; - } - } - return is; - }; - UAParserData.prototype.toString = function () { - var str = EMPTY; - for (var i in toString_props) { - if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { - str += (str ? ' ' : EMPTY) + this[toString_props[i]]; - } - } - return str ? str : UNDEF_TYPE; - }; - } - if (!NAVIGATOR_UADATA) { - UAParserData.prototype.then = function (cb) { - cb(this); - return this; + this.set(MAJOR, majorize(this.get(VERSION))); + break; + case UA_DEVICE: + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); + } + break; + case UA_OS: + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { + this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); + } + break; + case UA_RESULT: + var createUAParserItem = function (itemType) { + return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get(); }; - } - return new UAParserData(); - })(data); + this.set('ua', ua) + .set('ua_ch', uaCH) + .set(UA_BROWSER, createUAParserItem(UA_BROWSER)) + .set(UA_CPU, createUAParserItem(UA_CPU)) + .set(UA_DEVICE, createUAParserItem(UA_DEVICE)) + .set(UA_ENGINE, createUAParserItem(UA_ENGINE)) + .set(UA_OS, createUAParserItem(UA_OS)) + .get(); + } + return this; } UAParserItem.prototype.get = function (prop) { if (!prop) return this.data; return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; }; UAParserItem.prototype.parse = function () { - rgxMapper.call(this.data, this.ua, this.rgxMap); - return this; - }; - UAParserItem.prototype.set = function (prop, val) { - this.data[prop] = val; - return this; - }; - - function UAParserBrowser (ua, browserMap, uach) { - UAParserItem.call(this, [ - ua, - uach, - UA_BROWSER, - browserMap, - [NAME, VERSION, MAJOR], - [VERSION, MAJOR], - ' ?browser$', - [NAME, VERSION] - ]); - this.parse(); - // Brave-specific detection - if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { - this.set(NAME, 'Brave'); - } - this.set(MAJOR, majorize(this.get(VERSION))); - } - UAParserBrowser.prototype = new UAParserItem(); - UAParserBrowser.prototype.parseCH = function () { - var brands = this.uaCH[FULLVERLIST] || this.uaCH[BRANDS]; - if (brands) { - for (var i in brands) { - var brandName = brands[i].brand, - brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) { - this.set(NAME, strip(GOOGLE+' ', brandName)) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - } - } + if (this.itemType != UA_RESULT) { + rgxMapper.call(this.data, this.ua, this.rgxMap); } return this; }; - - function UAParserCPU (ua, cpuMap, uach) { - UAParserItem.call(this, [ - ua, - uach, - UA_CPU, - cpuMap, - [ARCHITECTURE], - [], - null, - [ARCHITECTURE] - ]); - this.parse(); - } - UAParserCPU.prototype = new UAParserItem(); - UAParserCPU.prototype.parseCH = function () { - var archName = this.uaCH[ARCHITECTURE]; - if (archName) { - archName += (archName && this.uaCH[BITNESS] == '64') ? '64' : EMPTY; - rgxMapper.call(this.data, archName, this.rgxMap); - } - return this; - }; - - function UAParserDevice (ua, deviceMap, uach) { - UAParserItem.call(this, [ - ua, - uach, - UA_DEVICE, - deviceMap, - [TYPE, MODEL, VENDOR], - [], - null, - [VENDOR, MODEL] - ]); - this.parse(); - if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { - this.set(TYPE, MOBILE); - } - // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (this.get(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { - this.set(MODEL, 'iPad') - .set(TYPE, TABLET); - } - } - UAParserDevice.prototype = new UAParserItem(); - UAParserDevice.prototype.parseCH = function () { - if (this.uaCH[MOBILE]) { - this.set(TYPE, MOBILE); - } - if (this.uaCH[MODEL]) { - this.set(MODEL, this.uaCH[MODEL]); + UAParserItem.prototype.parseCH = function () { + var ua = this.ua, + uaCH = this.uaCH, + rgxMap = this.rgxMap; + + switch (this.itemType) { + case UA_BROWSER: + var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; + if (brands) { + for (var i in brands) { + var brandName = brands[i].brand, + brandVersion = brands[i].version; + if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) { + this.set(NAME, strip(GOOGLE+' ', brandName)) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + } + } + break; + case UA_CPU: + var archName = uaCH[ARCHITECTURE]; + if (archName) { + if (archName && uaCH[BITNESS] == '64') archName += '64'; + rgxMapper.call(this.data, archName + ';', rgxMap); + } + break; + case UA_DEVICE: + if (uaCH[MOBILE]) { + this.set(TYPE, MOBILE); + } + if (uaCH[MODEL]) { + this.set(MODEL, uaCH[MODEL]); + } + break; + case UA_OS: + var osName = uaCH[PLATFORM]; + if(osName) { + var osVersion = uaCH[PLATFORMVER]; + if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10'); + this.set(NAME, osName) + .set(VERSION, osVersion); + } + break; + case UA_RESULT: + var createUAParserItemWithCH = function (itemType) { + return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get(); + }; + this.set('ua', ua) + .set('ua_ch', uaCH) + .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER)) + .set(UA_CPU, createUAParserItemWithCH(UA_CPU)) + .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE)) + .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE)) + .set(UA_OS, createUAParserItemWithCH(UA_OS)); } return this; }; - - function UAParserEngine (ua, engineMap) { - UAParserItem.call(this, [ - ua, - null, - UA_ENGINE, - engineMap, - [NAME, VERSION], - [VERSION], - null, - [NAME, VERSION] - ]); - this.parse(); - } - UAParserEngine.prototype = new UAParserItem(); - - function UAParserOS (ua, osMap, uach) { - UAParserItem.call(this, [ - ua, - uach, - UA_OS, - osMap, - [NAME, VERSION], - [VERSION], - ' ?os$', - [NAME, VERSION] - ]); - this.parse(); - if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { - this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); - } - } - UAParserOS.prototype = new UAParserItem(); - UAParserOS.prototype.parseCH = function () { - var osName = this.uaCH[PLATFORM]; - if(osName) { - var osVersion = this.uaCH[PLATFORMVER]; - osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion; - this.set(NAME, osName) - .set(VERSION, osVersion); - } + UAParserItem.prototype.set = function (prop, val) { + this.data[prop] = val; return this; }; - function UAParserResult (ua, resMap, uach) { - UAParserItem.call(this, [ua, uach, UA_RESULT, resMap]); - } - UAParserResult.prototype = new UAParserItem(); - function UAParser (ua, extensions, headers) { if (typeof ua === OBJ_TYPE) { @@ -1161,52 +1130,29 @@ const window = undefined; HTTP_UACH = new UAParserDataCH(headers, true), regexMap = extensions ? - extend(regexes, extensions) : - regexes; - - // public methods - this.getBrowser = function () { - return new UAParserBrowser(userAgent, regexMap[UA_BROWSER], HTTP_UACH).get(); - }; - - this.getCPU = function () { - return new UAParserCPU(userAgent, regexMap[UA_CPU], HTTP_UACH).get(); - }; - - this.getDevice = function () { - return new UAParserDevice(userAgent, regexMap[UA_DEVICE], HTTP_UACH).get(); - }; - - this.getEngine = function () { - return new UAParserEngine(userAgent, regexMap[UA_ENGINE]).get(); - }; - - this.getOS = function () { - return new UAParserOS(userAgent, regexMap[UA_OS], HTTP_UACH).get(); - }; + extend(defaultRegexes, extensions) : + defaultRegexes, - this.getResult = function () { - return new UAParserResult(userAgent, regexMap, HTTP_UACH) - .set('ua', userAgent) - .set('ua_ch', HTTP_UACH) - .set(UA_BROWSER, this.getBrowser()) - .set(UA_CPU, this.getCPU()) - .set(UA_DEVICE, this.getDevice()) - .set(UA_ENGINE, this.getEngine()) - .set(UA_OS, this.getOS()) - .get(); - }; - - this.getUA = function () { - return userAgent; - }; - - this.setUA = function (ua) { - userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; - return this; - }; + createUAParserItemFunc = function (itemType) { + return function () { + return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get(); + }; + }; - this.setUA(userAgent); + // public methods + assignFromEntries.call(this, [ + ['getBrowser', createUAParserItemFunc(UA_BROWSER)], + ['getCPU', createUAParserItemFunc(UA_CPU)], + ['getDevice', createUAParserItemFunc(UA_DEVICE)], + ['getEngine', createUAParserItemFunc(UA_ENGINE)], + ['getOS', createUAParserItemFunc(UA_OS)], + ['getResult', createUAParserItemFunc(UA_RESULT)], + ['getUA', function () { return userAgent; }], + ['setUA', function (ua) { + userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; + return this; + }] + ]).setUA(userAgent); return this; } diff --git a/test/es6-test.mjs b/test/es6-test.mjs index 4d8495872..442170cf9 100644 --- a/test/es6-test.mjs +++ b/test/es6-test.mjs @@ -1,4 +1,4 @@ -import { UAParser } from '../dist/ua-parser.mjs' +import { UAParser } from '../src/ua-parser.mjs' import * as assert from 'assert' describe('Returns', () => { From af65fd69600ec613bfb4709398302066cead4f66 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 28 Mar 2023 06:23:19 +0700 Subject: [PATCH 055/388] Fix #300 : Provide enums - initial work --- package.json | 8 +++- readme.md | 8 ++++ src/ua-parser-enum.js | 100 +++++++++++++++++++++++++++++++++++++++ src/ua-parser-enum.mjs | 103 +++++++++++++++++++++++++++++++++++++++++ test/es6-test.mjs | 14 +++++- 5 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 src/ua-parser-enum.js create mode 100644 src/ua-parser-enum.mjs diff --git a/package.json b/package.json index b00b5c5c8..86173847e 100644 --- a/package.json +++ b/package.json @@ -144,15 +144,21 @@ "main": "src/ua-parser.js", "module": "src/ua-parser.mjs", "exports": { + "." : { "require": "./src/ua-parser.js", "import": "./src/ua-parser.mjs" + }, + "./enums" : { + "require": "./src/ua-parser-enum.js", + "import": "./src/ua-parser-enum.mjs" + } }, "files": [ "dist", "src" ], "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8')\"", + "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// Source file: /src/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8')\"", "test": "jshint src/ua-parser.js && mocha -R nyan test", "test-ci": "jshint src/ua-parser.js && mocha -R spec test", "verup": "node ./node_modules/verup", diff --git a/readme.md b/readme.md index 7ad187291..071593de8 100644 --- a/readme.md +++ b/readme.md @@ -477,6 +477,14 @@ console.log('Server running at http://127.0.0.1:1337/'); ```js import { UAParser } from 'ua-parser-js'; +import { CPUArch, DeviceType } from 'ua-parser-js/enums'; + +const { cpu, device } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900'); + +console.log(browser.name) // Maemo Browser +console.log(cpu.is(CPUArch.ARM)) // true +console.log(device.is(DeviceType.MOBILE)) // true +console.log(device.model) // N900 ``` ## Using TypeScript diff --git a/src/ua-parser-enum.js b/src/ua-parser-enum.js new file mode 100644 index 000000000..63762525a --- /dev/null +++ b/src/ua-parser-enum.js @@ -0,0 +1,100 @@ +/////////////////////////////////////////////// +/* Enums for UAParser.js v2.0 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +////////////////////////////////////////////// + +const BrowserName = Object.freeze({ + CHROME : 'Chrome', + EDGE : 'Edge', + SAFARI : 'Safari', + FIREFOX : 'Firefox', + OPERA : 'Opera', + MOBILE_CHROME : 'Mobile Chrome', + MOBILE_SAFARI : 'Mobile Safari', + MOBILE_FIREFOX : 'Mobile Firefox', + ANDROID_BROWSER : 'Android Browser' + + // TODO : test! +}); + +const CPUArch = Object.freeze({ + IA32 : 'ia32', + AMD64 : 'amd64', + IA64 : 'ia64', + ARM : 'arm', + ARM64 : 'arm64', + ARMHF : 'armhf', + _68K : '68k', + AVR : 'avr', + IRIX : 'irix', + IRIX64 : 'irix64', + MIPS : 'mips', + MIPS64 : 'mips64', + PPC : 'ppc', + SPARC : 'sparc', + SPARC64 : 'sparc64' +}); + +const DeviceType = Object.freeze({ + MOBILE : 'mobile', + TABLET : 'tablet', + SMARTTV : 'smarttv', + CONSOLE : 'console', + WEARABLE: 'wearable', + EMBEDDED: 'embedded' +}); + +const DeviceVendor = Object.freeze({ + APPLE : 'Apple', + SAMSUNG : 'Samsung', + HUAWEI : 'Huawei', + XIAOMI : 'Xiaomi', + OPPO : 'OPPO', + VIVO : 'Vivo', + REALME : 'Realme', + LENOVO : 'Lenovo', + LG : 'LG' + + // TODO : test! +}); + +const EngineName = Object.freeze({ + AMAYA : 'Amaya', + BLINK : 'Blink', + EDGEHTML: 'EdgeHTML', + FLOW : 'Flow', + GECKO : 'Gecko', + GOANNA : 'Goanna', + ICAB : 'iCab', + KHTML : 'KHTML', + LINKS : 'Links', + LYNX : 'Lynx', + NETFRONT: 'NetFront', + NETSURF : 'NetSurf', + PRESTO : 'Presto', + TASMAN : 'Tasman', + TRIDENT : 'Trident', + W3M : 'w3m', + WEBKIT : 'WebKit' +}); + +const OSName = Object.freeze({ + WINDOWS : 'Windows', + LINUX : 'Linux', + MACOS : 'macOS', + IOS : 'iOS', + ANDROID : 'Android' + + // TODO : test! +}); + +module.exports = { + BrowserName, + CPUArch, + DeviceType, + DeviceVendor, + EngineName, + OSName +} \ No newline at end of file diff --git a/src/ua-parser-enum.mjs b/src/ua-parser-enum.mjs new file mode 100644 index 000000000..74e7537aa --- /dev/null +++ b/src/ua-parser-enum.mjs @@ -0,0 +1,103 @@ +// Generated ESM version of UAParser.js enums +// Source file: /src/ua-parser-enum.js + +/////////////////////////////////////////////// +/* Enums for UAParser.js v2.0 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +////////////////////////////////////////////// + +const BrowserName = Object.freeze({ + CHROME : 'Chrome', + EDGE : 'Edge', + SAFARI : 'Safari', + FIREFOX : 'Firefox', + OPERA : 'Opera', + MOBILE_CHROME : 'Mobile Chrome', + MOBILE_SAFARI : 'Mobile Safari', + MOBILE_FIREFOX : 'Mobile Firefox', + ANDROID_BROWSER : 'Android Browser' + + // TODO : test! +}); + +const CPUArch = Object.freeze({ + IA32 : 'ia32', + AMD64 : 'amd64', + IA64 : 'ia64', + ARM : 'arm', + ARM64 : 'arm64', + ARMHF : 'armhf', + _68K : '68k', + AVR : 'avr', + IRIX : 'irix', + IRIX64 : 'irix64', + MIPS : 'mips', + MIPS64 : 'mips64', + PPC : 'ppc', + SPARC : 'sparc', + SPARC64 : 'sparc64' +}); + +const DeviceType = Object.freeze({ + MOBILE : 'mobile', + TABLET : 'tablet', + SMARTTV : 'smarttv', + CONSOLE : 'console', + WEARABLE: 'wearable', + EMBEDDED: 'embedded' +}); + +const DeviceVendor = Object.freeze({ + APPLE : 'Apple', + SAMSUNG : 'Samsung', + HUAWEI : 'Huawei', + XIAOMI : 'Xiaomi', + OPPO : 'OPPO', + VIVO : 'Vivo', + REALME : 'Realme', + LENOVO : 'Lenovo', + LG : 'LG' + + // TODO : test! +}); + +const EngineName = Object.freeze({ + AMAYA : 'Amaya', + BLINK : 'Blink', + EDGEHTML: 'EdgeHTML', + FLOW : 'Flow', + GECKO : 'Gecko', + GOANNA : 'Goanna', + ICAB : 'iCab', + KHTML : 'KHTML', + LINKS : 'Links', + LYNX : 'Lynx', + NETFRONT: 'NetFront', + NETSURF : 'NetSurf', + PRESTO : 'Presto', + TASMAN : 'Tasman', + TRIDENT : 'Trident', + W3M : 'w3m', + WEBKIT : 'WebKit' +}); + +const OSName = Object.freeze({ + WINDOWS : 'Windows', + LINUX : 'Linux', + MACOS : 'macOS', + IOS : 'iOS', + ANDROID : 'Android' + + // TODO : test! +}); + +export { + BrowserName, + CPUArch, + DeviceType, + DeviceVendor, + EngineName, + OSName +} \ No newline at end of file diff --git a/test/es6-test.mjs b/test/es6-test.mjs index 442170cf9..e3e0cbc24 100644 --- a/test/es6-test.mjs +++ b/test/es6-test.mjs @@ -1,5 +1,6 @@ -import { UAParser } from '../src/ua-parser.mjs' -import * as assert from 'assert' +import { UAParser } from 'ua-parser-js'; +import { CPUArch, DeviceType, EngineName } from 'ua-parser-js/enums'; +import * as assert from 'assert'; describe('Returns', () => { it('getResult() should returns object', () => { @@ -14,4 +15,13 @@ describe('Returns', () => { os: { name: undefined, version: undefined } }); }); +}); + +describe('Enums', () => { + it('Can use enum', () => { + const { cpu, device, engine } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900'); + assert.strictEqual(cpu.is(CPUArch.ARM), true); + assert.strictEqual(device.is(DeviceType.MOBILE), true); + assert.strictEqual(engine.is(EngineName.GECKO), true); + }); }); \ No newline at end of file From 7a4fe6f4549fdd91412961293c6a90224c83bfba Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 28 Mar 2023 10:41:59 +0700 Subject: [PATCH 056/388] Fix #227 #237 #488 : Provide extensions - initial work --- package.json | 6 +++++- src/ua-parser-extension.js | 32 ++++++++++++++++++++++++++++++++ src/ua-parser-extension.mjs | 36 ++++++++++++++++++++++++++++++++++++ test/test-extension.js | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/ua-parser-extension.js create mode 100644 src/ua-parser-extension.mjs create mode 100644 test/test-extension.js diff --git a/package.json b/package.json index 86173847e..e10152a63 100644 --- a/package.json +++ b/package.json @@ -151,6 +151,10 @@ "./enums" : { "require": "./src/ua-parser-enum.js", "import": "./src/ua-parser-enum.mjs" + }, + "./extensions" : { + "require": "./src/ua-parser-extension.js", + "import": "./src/ua-parser-extension.mjs" } }, "files": [ @@ -158,7 +162,7 @@ "src" ], "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// Source file: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// Source file: /src/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8')\"", + "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && uglifyjs src/ua-parser-enum.js -o dist/ua-parser-enum.min.js --comments '/^ Enum/' && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/ua-parser-extension.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8')\"", "test": "jshint src/ua-parser.js && mocha -R nyan test", "test-ci": "jshint src/ua-parser.js && mocha -R spec test", "verup": "node ./node_modules/verup", diff --git a/src/ua-parser-extension.js b/src/ua-parser-extension.js new file mode 100644 index 000000000..fc4b3bc7b --- /dev/null +++ b/src/ua-parser-extension.js @@ -0,0 +1,32 @@ +/////////////////////////////////////////////// +/* Extensions for UAParser.js v2.0 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +////////////////////////////////////////////// + +const UAParser = require("./ua-parser") + +const Bots = Object.freeze({ + browser : [ + [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] + ] +}); + +const Emails = Object.freeze({ + browser : [ + [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'email']] + ] +}); + +const Tools = Object.freeze({ + browser : [ + [/(wget|curl|lynx)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'tool']] + ] +}); + +module.exports = { + Bots, + Emails, + Tools +} \ No newline at end of file diff --git a/src/ua-parser-extension.mjs b/src/ua-parser-extension.mjs new file mode 100644 index 000000000..a6b4032e1 --- /dev/null +++ b/src/ua-parser-extension.mjs @@ -0,0 +1,36 @@ +// Generated ESM version of UAParser.js extensions +// DO NOT EDIT THIS FILE! +// Source: /src/ua-parser-extension.js + +/////////////////////////////////////////////// +/* Extensions for UAParser.js v2.0 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +////////////////////////////////////////////// + +const UAParser = require("./ua-parser") + +const Bots = Object.freeze({ + browser : [ + [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] + ] +}); + +const Emails = Object.freeze({ + browser : [ + [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'email']] + ] +}); + +const Tools = Object.freeze({ + browser : [ + [/(wget|curl|lynx)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'tool']] + ] +}); + +export { + Bots, + Emails, + Tools +} \ No newline at end of file diff --git a/test/test-extension.js b/test/test-extension.js new file mode 100644 index 000000000..bbe2fd253 --- /dev/null +++ b/test/test-extension.js @@ -0,0 +1,35 @@ +const assert = require('assert'); +const safeRegex = require('safe-regex'); +const UAParser = require('ua-parser-js'); +const { Bots, Emails, Tools } = require('ua-parser-js/extensions'); + +describe('Bots', () => { + it('Can detect bots', () => { + const googleBot = 'Googlebot-Video/1.0'; + const msnBot = 'msnbot-media/1.1 (+http://search.msn.com/msnbot.htm)'; + const bingPreview = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b'; + const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)'; + const wget = 'Wget/1.21.1'; + const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)'; + const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; + const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; + + const botParser = new UAParser(Bots); + assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"}); + + // try merging Bots & Tools + const botsAndTools = { browser : [...Bots.browser, ...Tools.browser]}; + const botolParser = new UAParser(botsAndTools); + assert.deepEqual(botolParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"tool"}); + assert.deepEqual(botolParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); + + const emailParser = new UAParser(Emails); + assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); + assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); + }); +}); + +// TODO : check for safe-regex \ No newline at end of file From a6c85d0148836b3be8b6bb93e80d199d226d41df Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 29 Mar 2023 04:52:26 +0700 Subject: [PATCH 057/388] Add new engine: LibWeb + Add new OS: SerenityOS --- src/ua-parser-enum.js | 1 + src/ua-parser.js | 5 +++-- test/engine-test.json | 9 +++++++++ test/os-test.json | 9 +++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/ua-parser-enum.js b/src/ua-parser-enum.js index 63762525a..4b21b72ae 100644 --- a/src/ua-parser-enum.js +++ b/src/ua-parser-enum.js @@ -68,6 +68,7 @@ const EngineName = Object.freeze({ GECKO : 'Gecko', GOANNA : 'Goanna', ICAB : 'iCab', + LIBWEB : 'LibWeb', KHTML : 'KHTML', LINKS : 'Links', LYNX : 'Lynx', diff --git a/src/ua-parser.js b/src/ua-parser.js index bdf703603..b2f9ec2b2 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -771,7 +771,8 @@ /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna /ekioh(flow)\/([\w\.]+)/i, // Flow /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links - /(icab)[\/ ]([23]\.[\d\.]+)/i // iCab + /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab + /\b(libweb)/i ], [NAME, VERSION], [ /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko @@ -849,7 +850,7 @@ ], [[NAME, 'Solaris'], VERSION], [ /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX - /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX + /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS /(unix) ?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] diff --git a/test/engine-test.json b/test/engine-test.json index 6726d27d9..e75e539cc 100644 --- a/test/engine-test.json +++ b/test/engine-test.json @@ -53,6 +53,15 @@ "version" : "4.5.4" } }, + { + "desc" : "LibWeb", + "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb", + "expect" : + { + "name" : "LibWeb", + "version" : "undefined" + } + }, { "desc" : "NetFront", "ua" : "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", diff --git a/test/os-test.json b/test/os-test.json index b21274cbf..3700e5339 100644 --- a/test/os-test.json +++ b/test/os-test.json @@ -1204,5 +1204,14 @@ "name" : "Linspire", "version" : "1.5.0.4" } + }, + { + "desc" : "SerenityOS", + "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb", + "expect" : + { + "name" : "SerenityOS", + "version" : "undefined" + } } ] From 4af26c7a5e446c5f57353a7c22198210a97859e5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 29 Mar 2023 05:51:35 +0700 Subject: [PATCH 058/388] Add new browser: TikTok User-agent example: - https://user-agents.net/s/o2A0qRAZIN - https://explore.whatismybrowser.com/useragents/parse/241240920-android-webview-android-jny-l22-blink --- src/ua-parser.js | 2 ++ test/browser-test.json | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/ua-parser.js b/src/ua-parser.js index b2f9ec2b2..9d001c178 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -365,6 +365,8 @@ ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA']], [ + /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok + ], [VERSION, [NAME, 'TikTok']], [ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ diff --git a/test/browser-test.json b/test/browser-test.json index 0b0345f22..b8f7a6e80 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -1713,6 +1713,33 @@ "major" : "10" } }, + { + "desc" : "TikTok", + "ua" : "Mozilla/5.0 (Linux; Android 11; 21061119AG Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile Safari/537.36 trill_2022109040 JsSdk/1.0 NetType/MOBILE Channel/googleplay AppName/musical_ly app_version/21.9.4 ByteLocale/ru-RU ByteFullLocale/ru-RU Region/KG BytedanceWebview/d8a21c6", + "expect" : { + "name" : "TikTok", + "version": "21.9.4", + "major" : "21" + } + }, + { + "desc" : "TikTok", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 14_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 musical_ly_21.1.0 JsSdk/2.0 NetType/4G Channel/App Store ByteLocale/ru Region/RU ByteFullLocale/ru-RU isDarkMode/1 WKWebView/1 BytedanceWebview/d8a21c6", + "expect" : { + "name" : "TikTok", + "version": "21.1.0", + "major" : "21" + } + }, + { + "desc" : "TikTok", + "ua" : "Mozilla/5.0 (Linux; Android 10; STK-LX1 Build/HONORSTK-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile Safari/537.36 musical_ly_2022803040 JsSdk/1.0 NetType/WIFI Channel/huaweiadsglobal_int AppName/musical_ly app_version/28.3.4 ByteLocale/en ByteFullLocale/en Region/IQ Spark/1.2.7-alpha.8 AppVersion/28.3.4 PIA/1.5.11 BytedanceWebview/d8a21c6", + "expect" : { + "name" : "TikTok", + "version": "28.3.4", + "major" : "28" + } + }, { "desc" : "Chrome Mobile", "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36", From 894512c72ffa3ce6e0969f3d4a67740d88b82475 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 29 Mar 2023 10:02:56 +0700 Subject: [PATCH 059/388] Move some low-usage devices to extension as ExtraDevices --- src/ua-parser-extension.js | 97 ++++++++++- src/ua-parser.js | 57 +------ test/device-test.json | 342 ------------------------------------- test/test-extension.js | 1 + test/test-extension.json | 342 +++++++++++++++++++++++++++++++++++++ test/test.js | 14 +- 6 files changed, 444 insertions(+), 409 deletions(-) create mode 100644 test/test-extension.json diff --git a/src/ua-parser-extension.js b/src/ua-parser-extension.js index fc4b3bc7b..c9c64bd6c 100644 --- a/src/ua-parser-extension.js +++ b/src/ua-parser-extension.js @@ -5,28 +5,117 @@ MIT License */ ////////////////////////////////////////////// -const UAParser = require("./ua-parser") +const UAParser = require('ua-parser-js'); +const MODEL = 'model'; +const NAME = 'name'; +const TYPE = 'type'; +const VENDOR = 'vendor'; +const VERSION = 'version'; +const MOBILE = 'mobile'; +const TABLET = 'tablet'; const Bots = Object.freeze({ browser : [ - [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] + // Googlebot / BingBot / MSNBot / FacebookBot + [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] + ] +}); + +const ExtraDevices = Object.freeze({ + device : [ + [ + /(nook)[\w ]+build\/(\w+)/i, // Nook + /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak + /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets + /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets + /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets + /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(u304aa)/i // AT&T + ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ + + /\bsie-(\w*)/i // Siemens + ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ + + /\b(rct\w+) b/i // RCA Tablets + ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ + + /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets + ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ + + /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet + ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ + + /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet + ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ + + /\b(tm\d{3}\w+) b/i + ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ + + /\b(k88) b/i // ZTE K Series Tablet + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ + + /\b(nx\d{3}j) b/i // ZTE Nubia + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ + + /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ + + /\b(zur\d{3}) b/i // Swiss ZUR Tablet + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ + + /\b((zeki)?tb.*\b) b/i // Zeki Tablets + ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ + + /\b([yr]\d{2}) b/i, + /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet + ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [ + + /\b(ns-?\w{0,9}) b/i // Insignia Tablets + ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ + + /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets + ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ + + /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones + ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ + + /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones + ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ + + /\b(ph-1) /i // Essential PH-1 + ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ + + /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets + ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ + + /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets + ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ + + /\btu_(1491) b/i // Rotor Tablets + ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET] + ] ] }); const Emails = Object.freeze({ browser : [ - [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'email']] + // Microsoft Outlook / Thunderbird + [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] ] }); const Tools = Object.freeze({ browser : [ - [/(wget|curl|lynx)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'tool']] + // wget / curl / lynx + [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']] ] }); module.exports = { Bots, + ExtraDevices, Emails, Tools } \ No newline at end of file diff --git a/src/ua-parser.js b/src/ua-parser.js index 9d001c178..7ac4aa31c 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -71,10 +71,8 @@ SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', - SWISS = 'Swiss', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', - ZTE = 'ZTE', PREFIX_MOBILE = 'Mobile ', SUFFIX_BROWSER = ' Browser', CHROME = 'Chrome', @@ -266,10 +264,6 @@ '8.1' : 'NT 6.3', '10' : ['NT 6.4', 'NT 10.0'], 'RT' : 'ARM' - }, - archEquivalenceMap = { - 'amd64' : ['x86-64', 'x64'], - 'ia32' : ['x86'] }; ////////////// @@ -609,62 +603,13 @@ /(kobo)\s(ereader|touch)/i, // Kobo /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad - /(kindle)\/([\w\.]+)/i, // Kindle - /(nook)[\w ]+build\/(\w+)/i, // Nook - /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak - /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets - /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets - /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets - /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + /(kindle)\/([\w\.]+)/i // Kindle ], [VENDOR, MODEL, [TYPE, TABLET]], [ /(surface duo)/i // Surface Duo ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [ /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [ - /(u304aa)/i // AT&T - ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ - /\bsie-(\w*)/i // Siemens - ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ - /\b(rct\w+) b/i // RCA Tablets - ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ - /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets - ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ - /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet - ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ - /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet - ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ - /\b(tm\d{3}\w+) b/i - ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ - /\b(k88) b/i // ZTE K Series Tablet - ], [MODEL, [VENDOR, ZTE], [TYPE, TABLET]], [ - /\b(nx\d{3}j) b/i // ZTE Nubia - ], [MODEL, [VENDOR, ZTE], [TYPE, MOBILE]], [ - /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile - ], [MODEL, [VENDOR, SWISS], [TYPE, MOBILE]], [ - /\b(zur\d{3}) b/i // Swiss ZUR Tablet - ], [MODEL, [VENDOR, SWISS], [TYPE, TABLET]], [ - /\b((zeki)?tb.*\b) b/i // Zeki Tablets - ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ - /\b([yr]\d{2}) b/i, - /\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet - ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [ - /\b(ns-?\w{0,9}) b/i // Insignia Tablets - ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ - /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets - ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ - /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones - ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ - /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones - ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ - /\b(ph-1) /i // Essential PH-1 - ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ - /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets - ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ - /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets - ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ - /\btu_(1491) b/i // Rotor Tablets - ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [ /(shield[\w ]+) b/i // Nvidia Shield Tablets ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [ /(sprint) (\w+)/i // Sprint Phones diff --git a/test/device-test.json b/test/device-test.json index a7cb4b73f..1c54884c0 100644 --- a/test/device-test.json +++ b/test/device-test.json @@ -205,15 +205,6 @@ "type": "mobile" } }, - { - "desc": "Essential PH-1", - "ua": "Mozilla/5.0 (Linux; Android 9; PH-1 Build/PPR1.180905.036) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.86 Mobile Safari/537.36", - "expect": { - "vendor": "Essential", - "model": "PH-1", - "type": "mobile" - } - }, { "desc": "Fairphone 1U", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; FP1U Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", @@ -2727,294 +2718,6 @@ "type": "tablet" } }, - { - "desc": "RCA Voyager III Tablet", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; RCT6973W43 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "RCA", - "model": "RCT6973W43", - "type": "tablet" - } - }, - { - "desc": "RCA Voyager II Tablet", - "ua": "Mozilla/5.0 (Linux; Android 5.0; RCT6773W22B Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "RCA", - "model": "RCT6773W22B", - "type": "tablet" - } - }, - { - "desc": "Verizon Quanta Tablet", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; QMV7B Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "Verizon", - "model": "QMV7B", - "type": "tablet" - } - }, - { - "desc": "Verizon Ellipsis 8 Tablet", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; QTAQZ3 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "Verizon", - "model": "QTAQZ3", - "type": "tablet" - } - }, - { - "desc": "Verizon Ellipsis 8HD Tablet", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; QTASUN1 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Safari/537.36", - "expect": { - "vendor": "Verizon", - "model": "QTASUN1", - "type": "tablet" - } - }, - { - "desc": "Dell Venue 8 Tablet", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 8 3830 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "Dell", - "model": "Venue 8 3830", - "type": "tablet" - } - }, - { - "desc": "Dell Venue 7 Tablet", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 7 3730 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "Dell", - "model": "Venue 7 3730", - "type": "tablet" - } - }, - { - "desc": "Barnes & Noble Nook HD+ Tablet", - "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; Barnes & Noble Nook HD+ Build/JZO54K; CyanogenMod-10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "Barnes & Noble", - "model": "Nook HD+", - "type": "tablet" - } - }, - { - "desc": "Barnes & Noble V400 Tablet", - "ua": "Mozilla/5.0 (Linux; Android 4.0.4; BNTV400 Build/IMM76L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Safari/537.36", - "expect": { - "vendor": "Barnes & Noble", - "model": "V400", - "type": "tablet" - } - }, - { - "desc": "NuVision TM101A540N Tablet", - "ua": "Mozilla/5.0 (Linux; Android 5.1; TM101A540N Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/50.0.2661.86 Safari/537.36", - "expect": { - "vendor": "NuVision", - "model": "TM101A540N", - "type": "tablet" - } - }, - { - "desc": "ZTE-Z431", - "ua": "ZTE-Z431/1.4.0 NetFront/4.2 QTV5.1 Profile/MIDP-2.1 Configuration/CLDC-1.1", - "expect": { - "vendor": "ZTE", - "model": "Z431", - "type": "mobile" - } - }, - { - "desc": "ZTE", - "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; ZTE-Z740G Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "ZTE", - "model": "Z740G", - "type": "mobile" - } - }, - { - "desc": "ZTE K Series Tablet", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; K88 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "ZTE", - "model": "K88", - "type": "tablet" - } - }, - { - "desc": "ZTE Nubia Red Magic 3", - "ua": "Mozilla/5.0 (Linux; Android 9; NX629J Build/PKQ1.190321.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/45016 Mobile Safari/537.36 MMWEBID/4064 MicroMessenger/7.0.10.1580(0x27000A34) Process/tools NetType/WIFI Language/zh_CN ABI/arm64", - "expect": { - "vendor": "ZTE", - "model": "NX629J", - "type": "mobile" - } - }, - { - "desc": "ZTE Blade A5", - "ua": "Mozilla/5.0 (Linux; Android 9; ZTE Blade A5 2019) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "ZTE", - "model": "Blade A5 2019", - "type": "mobile" - } - }, - { - "desc": "ZTE BLADE V0730", - "ua": "Mozilla/5.0 (Linux; Android 6.0; ZTE BLADE V0730) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "ZTE", - "model": "BLADE V0730", - "type": "mobile" - } - }, - { - "desc": "ZTE B2017G", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE B2017G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", - "expect": { - "vendor": "ZTE", - "model": "B2017G", - "type": "mobile" - } - }, - { - "desc": "Swizz GEN610", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; GEN610 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36", - "expect": { - "vendor": "Swiss", - "model": "GEN610", - "type": "mobile" - } - }, - { - "desc": "Swizz ZUR700", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ZUR700 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Safari/537.36", - "expect": { - "vendor": "Swiss", - "model": "ZUR700", - "type": "tablet" - } - }, - { - "desc": "Zeki TB782b Tablet", - "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-US; TB782B Build/IMM76D) AppleWebKit/534.31 (KHTML, like Gecko) UCBrowser/9.0.2.299 U3/0.8.0 Mobile Safari/534.31", - "expect": { - "vendor": "Zeki", - "model": "TB782B", - "type": "tablet" - } - }, - { - "desc": "Dragon Touch Tablet", - "ua": "Mozilla/5.0 (Linux; Android 4.0.4; DT9138B Build/IMM76D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Mobile Safari/537.36", - "expect": { - "vendor": "Dragon Touch", - "model": "9138B", - "type": "tablet" - } - }, - { - "desc": "Insignia Tablet", - "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; NS-P08A7100 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "Insignia", - "model": "NS-P08A7100", - "type": "tablet" - } - }, - { - "desc": "Voice Xtreme V75", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2.1; en-us; V75 Build/JOP40D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "Voice", - "model": "V75", - "type": "mobile" - } - }, - { - "desc": "LvTel V11", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; V11 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36", - "expect": { - "vendor": "LvTel", - "model": "V11", - "type": "mobile" - } - }, - { - "desc": "Envizen Tablet V100MD", - "ua": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; V100MD Build/V100MD.20130816) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", - "expect": { - "vendor": "Envizen", - "model": "V100MD", - "type": "tablet" - } - }, - { - "desc": "Rotor Tablet", - "ua": "mozilla/5.0 (linux; android 5.0.1; tu_1491 build/lrx22c) applewebkit/537.36 (khtml, like gecko) chrome/43.0.2357.93 safari/537.36", - "expect": { - "vendor": "Rotor", - "model": "1491", - "type": "tablet" - } - }, - { - "desc": "MachSpeed Tablets", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Trio 7.85 vQ Build/Trio_7.85_vQ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36", - "expect": { - "vendor": "MachSpeed", - "model": "Trio 7.85 vQ", - "type": "tablet" - } - }, - { - "desc": "Trinity Tablets", - "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Trinity T101 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Safari/537.36", - "expect": { - "vendor": "Trinity", - "model": "T101", - "type": "tablet" - } - }, - { - "desc": "NextBook Next7", - "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; Next7P12 Build/IMM76I) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", - "expect": { - "vendor": "NextBook", - "model": "Next7P12", - "type": "tablet" - } - }, - { - "desc": "NextBook Tablets", - "ua": "Mozilla/5.0 (Linux; Android 5.0; NXA8QC116 Build/LRX21V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "NextBook", - "model": "NXA8QC116", - "type": "tablet" - } - }, - { - "desc": "Le Pan Tablets", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "Le Pan", - "model": "TC802A", - "type": "tablet" - } - }, - { - "desc": "Le Pan Tablets", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "Le Pan", - "model": "TC802A", - "type": "tablet" - } - }, { "desc": "Amazon Alexa Echo Show", "ua": "AlexaWebMediaPlayer/1.0.200641.0 (Linux;Android 5.1.1)", @@ -3078,15 +2781,6 @@ "type": "smarttv" } }, - { - "desc": "Gigaset Tablet", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Gigaset QV830 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", - "expect": { - "vendor": "Gigaset", - "model": "QV830", - "type": "tablet" - } - }, { "desc": "Amazon Fire 7", "ua": "Mozilla/5.0 (Linux; Android 5.1.1; KFAUWI) AppleWebKit/537.36 (KHTML, like Gecko) Silk/80.5.3 like Chrome/80.0.3987.162 Safari/537.36", @@ -3096,42 +2790,6 @@ "type": "tablet" } }, - { - "desc": "AT&T Radiant Core U304AA", - "ua": "Dalvik/2.1.0 (Linux; U; Android 9; U304AA Build/P00610)", - "expect": { - "vendor": "AT&T", - "model": "U304AA", - "type": "mobile" - } - }, - { - "desc": "Vodafone Smart Tab 4G", - "ua": "Mozilla/5.0 (Linux; Android 4.4.4; Vodafone Smart Tab 4G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", - "expect": { - "vendor": "Vodafone", - "model": "Smart Tab 4G", - "type": "tablet" - } - }, - { - "desc": "Vodafone Smart ultra 6", - "ua": "Mozilla/5.0 (Linux; Android 5.0.2; Vodafone Smart ultra 6 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36", - "expect": { - "vendor": "Vodafone", - "model": "Smart ultra 6", - "type": "tablet" - } - }, - { - "desc": "4ife 4K Smart TV Box", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; 4ife 4K Smart TV Box Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 Vinebre", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "smarttv" - } - }, { "desc": "FaceBook Mobile App", "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", diff --git a/test/test-extension.js b/test/test-extension.js index bbe2fd253..250ee6442 100644 --- a/test/test-extension.js +++ b/test/test-extension.js @@ -32,4 +32,5 @@ describe('Bots', () => { }); }); +// TODO : move test spec to JSON file // TODO : check for safe-regex \ No newline at end of file diff --git a/test/test-extension.json b/test/test-extension.json new file mode 100644 index 000000000..174c4adbe --- /dev/null +++ b/test/test-extension.json @@ -0,0 +1,342 @@ +[{ + "desc": "Essential PH-1", + "ua": "Mozilla/5.0 (Linux; Android 9; PH-1 Build/PPR1.180905.036) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.86 Mobile Safari/537.36", + "expect": { + "vendor": "Essential", + "model": "PH-1", + "type": "mobile" + } +}, +{ + "desc": "Gigaset Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Gigaset QV830 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Gigaset", + "model": "QV830", + "type": "tablet" + } +}, +{ + "desc": "RCA Voyager III Tablet", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; RCT6973W43 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "RCA", + "model": "RCT6973W43", + "type": "tablet" + } +}, +{ + "desc": "RCA Voyager II Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.0; RCT6773W22B Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "RCA", + "model": "RCT6773W22B", + "type": "tablet" + } +}, +{ + "desc": "Verizon Quanta Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; QMV7B Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Verizon", + "model": "QMV7B", + "type": "tablet" + } +}, +{ + "desc": "Verizon Ellipsis 8 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; QTAQZ3 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Verizon", + "model": "QTAQZ3", + "type": "tablet" + } +}, +{ + "desc": "Verizon Ellipsis 8HD Tablet", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; QTASUN1 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Safari/537.36", + "expect": { + "vendor": "Verizon", + "model": "QTASUN1", + "type": "tablet" + } +}, +{ + "desc": "Dell Venue 8 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 8 3830 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Dell", + "model": "Venue 8 3830", + "type": "tablet" + } +}, +{ + "desc": "Dell Venue 7 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 7 3730 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Dell", + "model": "Venue 7 3730", + "type": "tablet" + } +}, +{ + "desc": "Barnes & Noble Nook HD+ Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; Barnes & Noble Nook HD+ Build/JZO54K; CyanogenMod-10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Barnes & Noble", + "model": "Nook HD+", + "type": "tablet" + } +}, +{ + "desc": "Barnes & Noble V400 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.0.4; BNTV400 Build/IMM76L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Safari/537.36", + "expect": { + "vendor": "Barnes & Noble", + "model": "V400", + "type": "tablet" + } +}, +{ + "desc": "NuVision TM101A540N Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TM101A540N Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/50.0.2661.86 Safari/537.36", + "expect": { + "vendor": "NuVision", + "model": "TM101A540N", + "type": "tablet" + } +}, +{ + "desc": "ZTE-Z431", + "ua": "ZTE-Z431/1.4.0 NetFront/4.2 QTV5.1 Profile/MIDP-2.1 Configuration/CLDC-1.1", + "expect": { + "vendor": "ZTE", + "model": "Z431", + "type": "mobile" + } +}, +{ + "desc": "ZTE", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; ZTE-Z740G Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "ZTE", + "model": "Z740G", + "type": "mobile" + } +}, +{ + "desc": "ZTE K Series Tablet", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; K88 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "ZTE", + "model": "K88", + "type": "tablet" + } +}, +{ + "desc": "ZTE Nubia Red Magic 3", + "ua": "Mozilla/5.0 (Linux; Android 9; NX629J Build/PKQ1.190321.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/45016 Mobile Safari/537.36 MMWEBID/4064 MicroMessenger/7.0.10.1580(0x27000A34) Process/tools NetType/WIFI Language/zh_CN ABI/arm64", + "expect": { + "vendor": "ZTE", + "model": "NX629J", + "type": "mobile" + } +}, +{ + "desc": "ZTE Blade A5", + "ua": "Mozilla/5.0 (Linux; Android 9; ZTE Blade A5 2019) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "ZTE", + "model": "Blade A5 2019", + "type": "mobile" + } +}, +{ + "desc": "ZTE BLADE V0730", + "ua": "Mozilla/5.0 (Linux; Android 6.0; ZTE BLADE V0730) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "ZTE", + "model": "BLADE V0730", + "type": "mobile" + } +}, +{ + "desc": "ZTE B2017G", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE B2017G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", + "expect": { + "vendor": "ZTE", + "model": "B2017G", + "type": "mobile" + } +}, +{ + "desc": "Swizz GEN610", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; GEN610 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36", + "expect": { + "vendor": "Swiss", + "model": "GEN610", + "type": "mobile" + } +}, +{ + "desc": "Swizz ZUR700", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ZUR700 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Safari/537.36", + "expect": { + "vendor": "Swiss", + "model": "ZUR700", + "type": "tablet" + } +}, +{ + "desc": "Zeki TB782b Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-US; TB782B Build/IMM76D) AppleWebKit/534.31 (KHTML, like Gecko) UCBrowser/9.0.2.299 U3/0.8.0 Mobile Safari/534.31", + "expect": { + "vendor": "Zeki", + "model": "TB782B", + "type": "tablet" + } +}, +{ + "desc": "Dragon Touch Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.0.4; DT9138B Build/IMM76D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Mobile Safari/537.36", + "expect": { + "vendor": "Dragon Touch", + "model": "9138B", + "type": "tablet" + } +}, +{ + "desc": "Insignia Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; NS-P08A7100 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Insignia", + "model": "NS-P08A7100", + "type": "tablet" + } +}, +{ + "desc": "Voice Xtreme V75", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.1; en-us; V75 Build/JOP40D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Voice", + "model": "V75", + "type": "mobile" + } +}, +{ + "desc": "LvTel V11", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; V11 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36", + "expect": { + "vendor": "LvTel", + "model": "V11", + "type": "mobile" + } +}, +{ + "desc": "Envizen Tablet V100MD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; V100MD Build/V100MD.20130816) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "Envizen", + "model": "V100MD", + "type": "tablet" + } +}, +{ + "desc": "Rotor Tablet", + "ua": "mozilla/5.0 (linux; android 5.0.1; tu_1491 build/lrx22c) applewebkit/537.36 (khtml, like gecko) chrome/43.0.2357.93 safari/537.36", + "expect": { + "vendor": "Rotor", + "model": "1491", + "type": "tablet" + } +}, +{ + "desc": "MachSpeed Tablets", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Trio 7.85 vQ Build/Trio_7.85_vQ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36", + "expect": { + "vendor": "MachSpeed", + "model": "Trio 7.85 vQ", + "type": "tablet" + } +}, +{ + "desc": "Trinity Tablets", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Trinity T101 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Safari/537.36", + "expect": { + "vendor": "Trinity", + "model": "T101", + "type": "tablet" + } +}, +{ + "desc": "NextBook Next7", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; Next7P12 Build/IMM76I) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "NextBook", + "model": "Next7P12", + "type": "tablet" + } +}, +{ + "desc": "NextBook Tablets", + "ua": "Mozilla/5.0 (Linux; Android 5.0; NXA8QC116 Build/LRX21V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "NextBook", + "model": "NXA8QC116", + "type": "tablet" + } +}, +{ + "desc": "Le Pan Tablets", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Le Pan", + "model": "TC802A", + "type": "tablet" + } +}, +{ + "desc": "Le Pan Tablets", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Le Pan", + "model": "TC802A", + "type": "tablet" + } +}, +{ + "desc": "AT&T Radiant Core U304AA", + "ua": "Dalvik/2.1.0 (Linux; U; Android 9; U304AA Build/P00610)", + "expect": { + "vendor": "AT&T", + "model": "U304AA", + "type": "mobile" + } +}, +{ + "desc": "Vodafone Smart Tab 4G", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; Vodafone Smart Tab 4G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", + "expect": { + "vendor": "Vodafone", + "model": "Smart Tab 4G", + "type": "tablet" + } +}, +{ + "desc": "Vodafone Smart ultra 6", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; Vodafone Smart ultra 6 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36", + "expect": { + "vendor": "Vodafone", + "model": "Smart ultra 6", + "type": "tablet" + } +}, +{ + "desc": "4ife 4K Smart TV Box", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; 4ife 4K Smart TV Box Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 Vinebre", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } +}] \ No newline at end of file diff --git a/test/test.js b/test/test.js index 8077e82da..a7dbeea43 100644 --- a/test/test.js +++ b/test/test.js @@ -82,7 +82,7 @@ describe('Returns', function () { assert.deepEqual(new UAParser('').getResult(), { ua : '', - ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, + //ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, browser: { name: undefined, version: undefined, major: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, @@ -362,7 +362,7 @@ describe('Map UA-CH headers', function () { let engine = new UAParser(headers).getEngine().withClientHints(); let os = new UAParser(headers).getOS().withClientHints(); - let ua_ch = { +/* let ua_ch = { "architecture": "ARM", "bitness": "64", "brands": [ @@ -397,11 +397,11 @@ describe('Map UA-CH headers', function () { "model": "Pixel 99", "platform": "Windows", "platformVersion": "13" - }; + };*/ it('Can read from client-hints headers using `withClientHints()`', function () { - assert.deepEqual(uap.ua_ch, ua_ch); + //assert.deepEqual(uap.ua_ch, ua_ch); assert.strictEqual(uap.ua, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"); assert.strictEqual(uap.browser.name, "Chrome"); assert.strictEqual(uap.browser.version, "93.0.1.2"); @@ -436,7 +436,7 @@ describe('Map UA-CH headers', function () { engine = new UAParser(headers).getEngine(); os = new UAParser(headers).getOS(); - assert.deepEqual(uap.ua_ch, ua_ch); + //assert.deepEqual(uap.ua_ch, ua_ch); assert.strictEqual(uap.browser.name, "Chrome"); assert.strictEqual(uap.browser.version, "110.0.0.0"); assert.strictEqual(uap.browser.major, "110"); @@ -459,7 +459,7 @@ describe('Map UA-CH headers', function () { uap = UAParser(headers2).withClientHints(); - ua_ch = { +/* ua_ch = { "architecture": undefined, "bitness": undefined, "brands": undefined, @@ -470,7 +470,7 @@ describe('Map UA-CH headers', function () { "platformVersion": undefined }; - assert.deepEqual(uap.ua_ch, ua_ch); + assert.deepEqual(uap.ua_ch, ua_ch);*/ assert.strictEqual(uap.browser.name, "Chrome"); assert.strictEqual(uap.browser.version, "110.0.0.0"); assert.strictEqual(uap.browser.major, "110"); From 30de983043dc44e24980c4983bb4f0f250084226 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 29 Mar 2023 10:44:37 +0700 Subject: [PATCH 060/388] Alpha release of v2.0 --- bower.json | 2 +- changelog.md | 68 +++++++---- dist/ua-parser.min.js | 6 +- dist/ua-parser.pack.js | 6 +- package.js | 2 +- package.json | 20 ++-- readme.md | 109 ++++++++++-------- src/{ => enum}/ua-parser-enum.js | 2 +- src/{ => enum}/ua-parser-enum.mjs | 6 +- src/{ => extension}/ua-parser-extension.js | 3 +- src/extension/ua-parser-extension.mjs | 124 +++++++++++++++++++++ src/ua-parser-extension.mjs | 36 ------ src/ua-parser.js | 6 +- src/ua-parser.mjs | 73 ++---------- test/cpu-test.json | 8 ++ test/{es6-test.mjs => test-es6.mjs} | 2 +- 16 files changed, 279 insertions(+), 194 deletions(-) rename src/{ => enum}/ua-parser-enum.js (98%) rename src/{ => enum}/ua-parser-enum.mjs (94%) rename src/{ => extension}/ua-parser-extension.js (98%) create mode 100644 src/extension/ua-parser-extension.mjs delete mode 100644 src/ua-parser-extension.mjs rename test/{es6-test.mjs => test-es6.mjs} (84%) diff --git a/bower.json b/bower.json index c59773a0e..05b8579f8 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ua-parser-js", - "version": "0.7.33", + "version": "2.0.0-alpha.1", "authors": [ "Faisal Salman " ], diff --git a/changelog.md b/changelog.md index addaff4d3..a4aa6f1e3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,27 +1,42 @@ # UAParser.js Changelog +# Version 2.0.0-alpha.1 +- Breaking changes: + - Browser detection on mobile device: `Chrome => Mobile Chrome`, `Firefox => Mobile Firefox` + - OS detection: `Mac OS => macOS`, `Chromium OS => Chrome OS` +- Add some new methods in result object: + - Add support for client hints: `withClientHints()` + - Utility for easy comparison: `is()` + - Utility to print full-name: `toString()` +- Add support for ES module `import { UAParser } from 'ua-parser-js'` +- Provide Enums `'ua-parser-js/enums'` +- Provide Extensions `'ua-parser-js/extensions'` +- Add new browser: Heytap, TikTok +- Add new engine: LibWeb +- Add new OS: SerenityOS +- Improve browser detection: Yandex +- Improve device detection: iPhone, Amazon Echo +- Improve OS detection: iOS + # Version 0.7 / 1.0 Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. -## Version 0.7.30 / 1.0.1 +## Version 0.7.34 / 1.0.34 +- Fix Sharp Mobile detected as Huawei Tablet +- Fix IE8 bug +- Add new devices : Kobo e-Reader, Apple Watch, and some new SmartTV devices +- Add new OS : watchOS +- Improve browser detection : Kakao, Naver, Brave +- Improve device detection : Oculus, iPad +- Improve OS detection : Chrome OS +- Using navigator.userAgentData as fallback for device.type & os.name -- Add new browser : Obigo, UP.Browser, Klar -- Add new device : Oculus, Roku -- Add new OS: Maemo, HP-UX, Android-x86, Deepin, elementary OS, GhostBSD, Linspire, Manjaro, Sabayon -- Improve detection for Sony Xperia 1ii, LG Android TV, and some more devices -- Improve detection for ARM64 CPU -- Improve detection for Windows Mobile, Netscape, Mac on PowerPC -- Categorize PDA as mobile -- Fix Sharp devices misjudged as Huawei -- Fix trailing comma for ES3 compatibility -- Some code refactor - -## Version 0.7.31 / 1.0.2 +## Version 0.7.33 / 1.0.33 -- Fix OPPO Reno A5 incorrect detection -- Fix TypeError Bug -- Use AST to extract regexes and verify them with safe-regex +- Add new browser : Cobalt +- Identify Macintosh as an Apple device +- Fix ReDoS vulnerability ## Version 0.7.32 / 1.0.32 @@ -38,11 +53,24 @@ Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://g - Fix included commas in Safari / Mobile Safari version - Increase UA_MAX_LENGTH to 350 -## Version 0.7.33 / 1.0.33 +## Version 0.7.31 / 1.0.2 -- Add new browser : Cobalt -- Identify Macintosh as an Apple device -- Fix ReDoS vulnerability +- Fix OPPO Reno A5 incorrect detection +- Fix TypeError Bug +- Use AST to extract regexes and verify them with safe-regex + +## Version 0.7.30 / 1.0.1 + +- Add new browser : Obigo, UP.Browser, Klar +- Add new device : Oculus, Roku +- Add new OS: Maemo, HP-UX, Android-x86, Deepin, elementary OS, GhostBSD, Linspire, Manjaro, Sabayon +- Improve detection for Sony Xperia 1ii, LG Android TV, and some more devices +- Improve detection for ARM64 CPU +- Improve detection for Windows Mobile, Netscape, Mac on PowerPC +- Categorize PDA as mobile +- Fix Sharp devices misjudged as Huawei +- Fix trailing comma for ES3 compatibility +- Some code refactor # Version 0.8 diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 71c56fb0c..2936c2e63 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v1.1.34 - Copyright © 2012-2021 Faisal Salman +/* UAParser.js v2.0.0-alpha.1 + Copyright © 2012-2023 Faisal Salman MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="0.7.32",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",UA_MAX_LENGTH=350;var AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",BROWSER="Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",OPERA="Opera",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",FACEBOOK="Facebook";var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){mergedRegexes[i]=extensions[i].concat(regexes[i])}else{mergedRegexes[i]=regexes[i]}}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;jUA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this};this.setUA(_ua);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.1",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]&&NAVIGATOR_UADATA[PLATFORM]!="Unknown"){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)||/chromi/i.test(this.get(NAME))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;return this};function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 9318cbcef..c5d1e9c43 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v1.1.34 - Copyright © 2012-2021 Faisal Salman +/* UAParser.js v2.0.0-alpha.1 + Copyright © 2012-2023 Faisal Salman MIT License */ -!function(a,d){"use strict";function i(i){for(var e={},o=0;oS?L(i,S):i,this},this.setUA(o),this}Q.prototype.is=function(i){for(var e in this.propIs)if(o(this[this.propIs[e]],this.rgxIs)==o(i,this.rgxIs))return!0;return!1},Q.prototype.toString=function(){var i,e="";for(i in this.propToString)typeof this[this.propToString[i]]!==s&&(e+=(e?" ":"")+this[this.propToString[i]]);return e||s},Y.prototype=new Q([l,h],[[l],/\s?browser$/i]),J.prototype=new Q([f],[[f]]),ii.prototype=new Q([m,r],[[p,r,m]]),ei.prototype=new Q([l,h],[[l]]),oi.prototype=new Q([l,h],[[l],/\s?os$/i]),ti.VERSION="0.7.32",ti.BROWSER=i([l,h,w]),ti.CPU=i([f]),ti.DEVICE=i([r,m,p,g,v,k,x,y,_]),ti.ENGINE=ti.OS=i([l,h]),typeof exports!==s?(typeof module!==s&&module.exports&&(exports=module.exports=ti),exports.UAParser=ti):typeof define==u&&define.amd?define(function(){return ti}):typeof a!==s&&(a.UAParser=ti);var ai,ni=typeof a!==s&&(a.jQuery||a.Zepto);ni&&!ni.ua&&(ai=new ti,ni.ua=ai.getResult(),ni.ua.get=function(){return ai.getUA()},ni.ua.set=function(i){ai.setUA(i);var e,o=ai.getResult();for(e in o)ni.ua[e]=o[e]})}("object"==typeof window?window:this); +!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?yi(i,S):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=L&&w.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,r=i[r].version;/not.a.brand/i.test(a)&&!/chromi/i.test(this.get(m))||this.set(m,xi(K+" ",a)).set(v,r).set(h,vi(r))}break;case B:var s=t[x];s&&(s&&"64"==t[A]&&(s+="64"),w.call(this.data,s+";",o));break;case F:t[k]&&this.set(g,k),t[f]&&this.set(f,t[f]);break;case D:s=t[N];s&&(n=t[z],s==ui&&(n=13<=parseInt(vi(n),10)?"11":"10"),this.set(m,s).set(v,n));break;case L:var n=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,n(V)).set(B,n(B)).set(F,n(F)).set($,n($)).set(D,n(D))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.1",zi.BROWSER=e([m,v,h]),zi.CPU=e([x]),zi.DEVICE=e([f,o,g,r,k,n,a,y,_]),zi.ENGINE=zi.OS=e([m,v]),typeof exports!==d?(typeof module!==d&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===c&&define.amd?define(function(){return zi}):typeof i!==d&&(i.UAParser=zi);var Ai,Oi=typeof i!==d&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package.js b/package.js index a3bcdb348..3f2ddf1cb 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'faisalman:ua-parser-js', - version: '0.7.33', + version: '2.0.0-alpha.1', summary: 'Lightweight JavaScript-based user-agent string parser', git: 'https://github.com/faisalman/ua-parser-js.git', documentation: 'readme.md' diff --git a/package.json b/package.json index e10152a63..37191dcb7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "0.7.33", + "version": "2.0.0-alpha.1", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment", "keywords": [ @@ -139,22 +139,22 @@ "yuanyang ", "Yun Young-jin ", "Zach Bjornson " - ], + ], "type": "commonjs", "main": "src/ua-parser.js", "module": "src/ua-parser.mjs", "exports": { - "." : { + ".": { "require": "./src/ua-parser.js", "import": "./src/ua-parser.mjs" }, - "./enums" : { - "require": "./src/ua-parser-enum.js", - "import": "./src/ua-parser-enum.mjs" + "./enums": { + "require": "./src/enum/ua-parser-enum.js", + "import": "./src/enum/ua-parser-enum.mjs" }, - "./extensions" : { - "require": "./src/ua-parser-extension.js", - "import": "./src/ua-parser-extension.mjs" + "./extensions": { + "require": "./src/extension/ua-parser-extension.js", + "import": "./src/extension/ua-parser-extension.mjs" } }, "files": [ @@ -162,7 +162,7 @@ "src" ], "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && uglifyjs src/ua-parser-enum.js -o dist/ua-parser-enum.min.js --comments '/^ Enum/' && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/ua-parser-extension.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8')\"", + "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"", "test": "jshint src/ua-parser.js && mocha -R nyan test", "test-ci": "jshint src/ua-parser.js && mocha -R spec test", "verup": "node ./node_modules/verup", diff --git a/readme.md b/readme.md index 071593de8..ad229f322 100644 --- a/readme.md +++ b/readme.md @@ -43,8 +43,11 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro --- +# Version 2.0 +What's new & breaking, please read [CHANGELOG](changelog.md) before upgrading. + # Documentation -### UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)]) +### UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]) In the browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in' nodejs for the function to work. Usually you can find the user agent in: `request.headers["user-agent"]`. @@ -52,7 +55,7 @@ In the browser environment you dont need to pass the user-agent string to the fu ## Constructor When you call `UAParser` with the `new` keyword, `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string. Like so: -* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])` +* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])` ```js let parser = new UAParser("your user-agent here"); // you need to pass the user-agent for nodejs console.log(parser); // {} @@ -65,22 +68,19 @@ console.log(parserResults); "os" : {}, "device" : {}, "cpu" : {} - - // since@1.1 - ,"ua_ch" : {} } */ ``` When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results. -* `UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])` - * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }` +* `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])` + * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` ## Methods #### Methods table The methods are self explanatory, here's a small overview on all the available methods: * `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os: -`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`. +`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`. * `getBrowser()` - returns the browser name and version. * `getDevice()` - returns the device model, type, vendor. @@ -93,7 +93,7 @@ The methods are self explanatory, here's a small overview on all the available m --- * `getResult()` - * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }` + * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` * `getBrowser()` * returns `{ name: '', version: '' }` @@ -113,7 +113,7 @@ NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb, Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris, Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, [Mobile] Safari, Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim, -SlimBrowser, Swiftfox, Tesla, Tizen Browser, UCBrowser, UP.Browser, Viera, +SlimBrowser, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser, UP.Browser, Viera, Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ... # 'browser.version' determined dynamically @@ -148,8 +148,8 @@ Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE ```sh # Possible 'engine.name' -Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, Links, Lynx, NetFront, -NetSurf, Presto, Tasman, Trident, w3m, WebKit +Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, LibWeb, Links, Lynx, +NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit # 'engine.version' determined dynamically ``` @@ -165,8 +165,9 @@ Fuchsia, Gentoo, GhostBSD, GNU, Haiku, HarmonyOS, HP-UX, Hurd, iOS, Joli, KaiOS, Linpus, Linspire,Linux, Mac OS, Maemo, Mageia, Mandriva, Manjaro, MeeGo, Minix, Mint, Morph OS, NetBSD, NetRange, NetTV, Nintendo, OpenBSD, OpenVMS, OS/2, Palm, PC-BSD, PCLinuxOS, Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS, -RISC OS, Sabayon, Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen, -Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwalk, ... +RISC OS, Sabayon, Sailfish, SerenityOS, Series40, Slackware, Solaris, SUSE, Symbian, +Tizen, Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], +Zenwalk, ... # 'os.version' determined dynamically ``` @@ -186,10 +187,10 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal * set UA string to be parsed * returns current instance -#### * `is():boolean` utility `since@1.1` +#### * `is():boolean` utility `since@2.0` ```js -// Is just a shorthand to check whether specified item has a property with equals value (case-insensitive) +// Is just a shorthand comparison to check whether the value of specified item equals one of its properties (in a case-insensitive way) // so that instead of write it using `==` operator like this: let ua = UAParser(); @@ -202,15 +203,10 @@ if (device.type == "smarttv" || device.vendor == "Samsung") {} // we can also write the comparison above into as follow: if (device.is("mobile") && !os.is("iOS")) {} -if (device.is("smarttv") || device.is("Samsung")) {} +if (device.is("SmartTV") || device.is("SaMsUnG")) {} /* - Each properties will be checked in this particular order: - * browser : name - * cpu : architecture - * device : type, model, vendor - * engine : name - * os : name + For device, properties will be checked in this particular order: type, model, vendor */ // Another examples: @@ -250,7 +246,7 @@ let engine = uap.getEngine(); engine.is("Blink"); // true ``` -#### * `toString():string` utility `since@1.1` +#### * `toString():string` utility `since@2.0` ```js // Retrieve full-name values as a string @@ -291,11 +287,12 @@ engine.version; // "28.0.1500.95" engine.toString(); // "Blink 28.0.1500.95" ``` -#### * `withClientHints():Promise|Thenable` `since@1.1` +#### * `withClientHints():Promise|Thenable` `since@2.0` -Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`. In nodejs-environment / browser-environment with non-secure context or without client-hints support (basically anything that's not chromium-based) this will return the updated data as a `Thenable` (can be chained with `then()`). +Recently Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`. ```js +// client-side example (async function () { let ua = new UAParser(); @@ -314,12 +311,43 @@ Unlike reading user-agent data, accessing client-hints data in browser-environme })(); ``` +Along with `User-Agent` HTTP header, Chrome also sends this client-hints data by default under `Sec-CH-UA-*` HTTP headers in each request. In server-side development, you can capture this extra information by passing the `req.headers` to `UAParser()` (see examples below). When using `withClientHints()` in nodejs environment and browser without client-hints support (basically anything that's not Chromium-based) the updated data will be returned as a `Thenable` (has `then()` method). + +```js +// server-side example + +// Suppose we got a request having these HTTP headers: +const request = { + headers : { + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36', + + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : 'Galaxy S3 Marketing', + 'sec-ch-ua-platform' : 'Android' + } +}; + +const result1 = UAParser(request.headers); // parse only "user-agent" header +const result2 = UAParser(request.headers).withClientHints(); // update with "sec-ch-ua" headers + +console.log(result1.os.name); // "Linux" +console.log(result1.device.type); // undefined +console.log(result1.device.model); // undefined + +console.log(result2.os.name); // "Android" +console.log(result2.device.type); // "mobile" +console.log(result2.device.model); // "Galaxy S3 Marketing" + +new UAParser(request.headers).getBrowser().withClientHints().then((browser) => { + console.log(browser.toString()); // Chrome 110.0.0.0 +}); +``` ## Extending Regex If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own. -* `UAParser([uastring,] extensions [,headers:object(since@1.1)])` +* `UAParser([uastring,] extensions [,headers:object(since@2.0)])` ```js // Example: @@ -389,18 +417,6 @@ console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "1 cpu: { architecture: "" } - - // added since@1.1: - ,ua_ch: { - architecture: "", - brands: "", - bitness: "", - fullVersionList: "", - mobile: "", - model: "", - platform: "", - platformVersion: "" - } } */ // Default result depends on current window.navigator.userAgent value @@ -453,7 +469,7 @@ http.createServer(function (req, res) { // get user-agent header var ua = uap(req.headers['user-agent']); - /* // BEGIN since@1.1 - you can also pass client-hints data to UAParser + /* // BEGIN since@2.0 - you can also pass client-hints data to UAParser // note: only works in secure context (https:// or localhost or file://) @@ -463,7 +479,7 @@ http.createServer(function (req, res) { var ua = uap(req.headers); - // END since@1.1 */ + // END since@2.0 */ // write the result as response res.end(JSON.stringify(ua, null, ' ')); @@ -477,14 +493,13 @@ console.log('Server running at http://127.0.0.1:1337/'); ```js import { UAParser } from 'ua-parser-js'; -import { CPUArch, DeviceType } from 'ua-parser-js/enums'; -const { cpu, device } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900'); +const { browser, cpu, device } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900'); -console.log(browser.name) // Maemo Browser -console.log(cpu.is(CPUArch.ARM)) // true -console.log(device.is(DeviceType.MOBILE)) // true -console.log(device.model) // N900 +console.log(browser.name); // Maemo Browser +console.log(cpu.is('arm')); // true +console.log(device.is('mobile')); // true +console.log(device.model); // N900 ``` ## Using TypeScript diff --git a/src/ua-parser-enum.js b/src/enum/ua-parser-enum.js similarity index 98% rename from src/ua-parser-enum.js rename to src/enum/ua-parser-enum.js index 4b21b72ae..8333eaf8f 100644 --- a/src/ua-parser-enum.js +++ b/src/enum/ua-parser-enum.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0 +/* Enums for UAParser.js v2.0.0-alpha.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/ua-parser-enum.mjs b/src/enum/ua-parser-enum.mjs similarity index 94% rename from src/ua-parser-enum.mjs rename to src/enum/ua-parser-enum.mjs index 74e7537aa..7708532fa 100644 --- a/src/ua-parser-enum.mjs +++ b/src/enum/ua-parser-enum.mjs @@ -1,8 +1,9 @@ // Generated ESM version of UAParser.js enums -// Source file: /src/ua-parser-enum.js +// DO NOT EDIT THIS FILE! +// Source: /src/enum/ua-parser-enum.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0 +/* Enums for UAParser.js v2.0.0-alpha.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ @@ -71,6 +72,7 @@ const EngineName = Object.freeze({ GECKO : 'Gecko', GOANNA : 'Goanna', ICAB : 'iCab', + LIBWEB : 'LibWeb', KHTML : 'KHTML', LINKS : 'Links', LYNX : 'Lynx', diff --git a/src/ua-parser-extension.js b/src/extension/ua-parser-extension.js similarity index 98% rename from src/ua-parser-extension.js rename to src/extension/ua-parser-extension.js index c9c64bd6c..dfa90ed50 100644 --- a/src/ua-parser-extension.js +++ b/src/extension/ua-parser-extension.js @@ -1,11 +1,10 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0 +/* Extensions for UAParser.js v2.0.0-alpha.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ ////////////////////////////////////////////// -const UAParser = require('ua-parser-js'); const MODEL = 'model'; const NAME = 'name'; const TYPE = 'type'; diff --git a/src/extension/ua-parser-extension.mjs b/src/extension/ua-parser-extension.mjs new file mode 100644 index 000000000..8b645fc74 --- /dev/null +++ b/src/extension/ua-parser-extension.mjs @@ -0,0 +1,124 @@ +// Generated ESM version of UAParser.js extensions +// DO NOT EDIT THIS FILE! +// Source: /src/extension/ua-parser-extension.js + +/////////////////////////////////////////////// +/* Extensions for UAParser.js v2.0.0-alpha.1 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +////////////////////////////////////////////// + +const MODEL = 'model'; +const NAME = 'name'; +const TYPE = 'type'; +const VENDOR = 'vendor'; +const VERSION = 'version'; +const MOBILE = 'mobile'; +const TABLET = 'tablet'; + +const Bots = Object.freeze({ + browser : [ + // Googlebot / BingBot / MSNBot / FacebookBot + [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] + ] +}); + +const ExtraDevices = Object.freeze({ + device : [ + [ + /(nook)[\w ]+build\/(\w+)/i, // Nook + /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak + /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets + /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets + /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets + /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(u304aa)/i // AT&T + ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ + + /\bsie-(\w*)/i // Siemens + ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ + + /\b(rct\w+) b/i // RCA Tablets + ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ + + /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets + ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ + + /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet + ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ + + /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet + ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ + + /\b(tm\d{3}\w+) b/i + ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ + + /\b(k88) b/i // ZTE K Series Tablet + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ + + /\b(nx\d{3}j) b/i // ZTE Nubia + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ + + /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ + + /\b(zur\d{3}) b/i // Swiss ZUR Tablet + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ + + /\b((zeki)?tb.*\b) b/i // Zeki Tablets + ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ + + /\b([yr]\d{2}) b/i, + /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet + ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [ + + /\b(ns-?\w{0,9}) b/i // Insignia Tablets + ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ + + /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets + ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ + + /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones + ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ + + /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones + ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ + + /\b(ph-1) /i // Essential PH-1 + ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ + + /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets + ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ + + /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets + ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ + + /\btu_(1491) b/i // Rotor Tablets + ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET] + ] + ] +}); + +const Emails = Object.freeze({ + browser : [ + // Microsoft Outlook / Thunderbird + [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] + ] +}); + +const Tools = Object.freeze({ + browser : [ + // wget / curl / lynx + [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']] + ] +}); + +export { + Bots, + ExtraDevices, + Emails, + Tools +} \ No newline at end of file diff --git a/src/ua-parser-extension.mjs b/src/ua-parser-extension.mjs deleted file mode 100644 index a6b4032e1..000000000 --- a/src/ua-parser-extension.mjs +++ /dev/null @@ -1,36 +0,0 @@ -// Generated ESM version of UAParser.js extensions -// DO NOT EDIT THIS FILE! -// Source: /src/ua-parser-extension.js - -/////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0 - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -////////////////////////////////////////////// - -const UAParser = require("./ua-parser") - -const Bots = Object.freeze({ - browser : [ - [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] - ] -}); - -const Emails = Object.freeze({ - browser : [ - [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'email']] - ] -}); - -const Tools = Object.freeze({ - browser : [ - [/(wget|curl|lynx)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'tool']] - ] -}); - -export { - Bots, - Emails, - Tools -} \ No newline at end of file diff --git a/src/ua-parser.js b/src/ua-parser.js index 7ac4aa31c..6a82ed1ad 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v1.1.34 +/* UAParser.js v2.0.0-alpha.1 Copyright © 2012-2023 Faisal Salman MIT License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -17,7 +17,7 @@ ///////////// - var LIBVERSION = '1.1.34', + var LIBVERSION = '2.0.0-alpha.1', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -960,7 +960,6 @@ return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get(); }; this.set('ua', ua) - .set('ua_ch', uaCH) .set(UA_BROWSER, createUAParserItem(UA_BROWSER)) .set(UA_CPU, createUAParserItem(UA_CPU)) .set(UA_DEVICE, createUAParserItem(UA_DEVICE)) @@ -1029,7 +1028,6 @@ return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get(); }; this.set('ua', ua) - .set('ua_ch', uaCH) .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER)) .set(UA_CPU, createUAParserItemWithCH(UA_CPU)) .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE)) diff --git a/src/ua-parser.mjs b/src/ua-parser.mjs index 1821d97dc..e0d93d0d8 100644 --- a/src/ua-parser.mjs +++ b/src/ua-parser.mjs @@ -1,10 +1,11 @@ // Generated ESM version of UAParser.js -// Source file: /src/ua-parser.js +// DO NOT EDIT THIS FILE! +// Source: /src/ua-parser.js const window = undefined; ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v1.1.34 +/* UAParser.js v2.0.0-alpha.1 Copyright © 2012-2023 Faisal Salman MIT License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -20,7 +21,7 @@ const window = undefined; ///////////// - var LIBVERSION = '1.1.34', + var LIBVERSION = '2.0.0-alpha.1', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -74,10 +75,8 @@ const window = undefined; SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', - SWISS = 'Swiss', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', - ZTE = 'ZTE', PREFIX_MOBILE = 'Mobile ', SUFFIX_BROWSER = ' Browser', CHROME = 'Chrome', @@ -269,10 +268,6 @@ const window = undefined; '8.1' : 'NT 6.3', '10' : ['NT 6.4', 'NT 10.0'], 'RT' : 'ARM' - }, - archEquivalenceMap = { - 'amd64' : ['x86-64', 'x64'], - 'ia32' : ['x86'] }; ////////////// @@ -368,6 +363,8 @@ const window = undefined; ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA']], [ + /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok + ], [VERSION, [NAME, 'TikTok']], [ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ @@ -610,62 +607,13 @@ const window = undefined; /(kobo)\s(ereader|touch)/i, // Kobo /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad - /(kindle)\/([\w\.]+)/i, // Kindle - /(nook)[\w ]+build\/(\w+)/i, // Nook - /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak - /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets - /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets - /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets - /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + /(kindle)\/([\w\.]+)/i // Kindle ], [VENDOR, MODEL, [TYPE, TABLET]], [ /(surface duo)/i // Surface Duo ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [ /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [ - /(u304aa)/i // AT&T - ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ - /\bsie-(\w*)/i // Siemens - ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ - /\b(rct\w+) b/i // RCA Tablets - ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ - /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets - ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ - /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet - ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ - /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet - ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ - /\b(tm\d{3}\w+) b/i - ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ - /\b(k88) b/i // ZTE K Series Tablet - ], [MODEL, [VENDOR, ZTE], [TYPE, TABLET]], [ - /\b(nx\d{3}j) b/i // ZTE Nubia - ], [MODEL, [VENDOR, ZTE], [TYPE, MOBILE]], [ - /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile - ], [MODEL, [VENDOR, SWISS], [TYPE, MOBILE]], [ - /\b(zur\d{3}) b/i // Swiss ZUR Tablet - ], [MODEL, [VENDOR, SWISS], [TYPE, TABLET]], [ - /\b((zeki)?tb.*\b) b/i // Zeki Tablets - ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ - /\b([yr]\d{2}) b/i, - /\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet - ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [ - /\b(ns-?\w{0,9}) b/i // Insignia Tablets - ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ - /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets - ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ - /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones - ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ - /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones - ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ - /\b(ph-1) /i // Essential PH-1 - ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ - /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets - ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ - /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets - ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ - /\btu_(1491) b/i // Rotor Tablets - ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [ /(shield[\w ]+) b/i // Nvidia Shield Tablets ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [ /(sprint) (\w+)/i // Sprint Phones @@ -774,7 +722,8 @@ const window = undefined; /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna /ekioh(flow)\/([\w\.]+)/i, // Flow /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links - /(icab)[\/ ]([23]\.[\d\.]+)/i // iCab + /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab + /\b(libweb)/i ], [NAME, VERSION], [ /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko @@ -852,7 +801,7 @@ const window = undefined; ], [[NAME, 'Solaris'], VERSION], [ /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX - /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX + /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS /(unix) ?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] @@ -1015,7 +964,6 @@ const window = undefined; return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get(); }; this.set('ua', ua) - .set('ua_ch', uaCH) .set(UA_BROWSER, createUAParserItem(UA_BROWSER)) .set(UA_CPU, createUAParserItem(UA_CPU)) .set(UA_DEVICE, createUAParserItem(UA_DEVICE)) @@ -1084,7 +1032,6 @@ const window = undefined; return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get(); }; this.set('ua', ua) - .set('ua_ch', uaCH) .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER)) .set(UA_CPU, createUAParserItemWithCH(UA_CPU)) .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE)) diff --git a/test/cpu-test.json b/test/cpu-test.json index cf314f9c8..2a7d18920 100644 --- a/test/cpu-test.json +++ b/test/cpu-test.json @@ -206,5 +206,13 @@ { "architecture" : "irix64" } + }, + { + "desc" : "68k", + "ua" : "'Mozilla/1.1 (Macintosh; U; 68K)'", + "expect" : + { + "architecture" : "68k" + } } ] diff --git a/test/es6-test.mjs b/test/test-es6.mjs similarity index 84% rename from test/es6-test.mjs rename to test/test-es6.mjs index e3e0cbc24..8165ec996 100644 --- a/test/es6-test.mjs +++ b/test/test-es6.mjs @@ -7,7 +7,7 @@ describe('Returns', () => { assert.deepEqual(new UAParser('').getResult(), { ua : '', - ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, + //ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, browser: { name: undefined, version: undefined, major: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, From b09878934fce1de9bcadace72cc3a6fd6c613e34 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 29 Mar 2023 22:52:56 +0700 Subject: [PATCH 061/388] Prevent altering the result when supplied user-agent is different from current user-agent --- dist/ua-parser.min.js | 2 +- dist/ua-parser.pack.js | 2 +- readme.md | 2 +- src/ua-parser.js | 9 +++++---- src/ua-parser.mjs | 9 +++++---- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 2936c2e63..9bba36131 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-alpha.1 Copyright © 2012-2023 Faisal Salman MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.1",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]&&NAVIGATOR_UADATA[PLATFORM]!="Unknown"){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)||/chromi/i.test(this.get(NAME))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;return this};function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.1",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(isSelfNav&&!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)||/chromi/i.test(this.get(NAME))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;return this};function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index c5d1e9c43..ca1bc8f58 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-alpha.1 Copyright © 2012-2023 Faisal Salman MIT License */ -!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?yi(i,S):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=L&&w.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,r=i[r].version;/not.a.brand/i.test(a)&&!/chromi/i.test(this.get(m))||this.set(m,xi(K+" ",a)).set(v,r).set(h,vi(r))}break;case B:var s=t[x];s&&(s&&"64"==t[A]&&(s+="64"),w.call(this.data,s+";",o));break;case F:t[k]&&this.set(g,k),t[f]&&this.set(f,t[f]);break;case D:s=t[N];s&&(n=t[z],s==ui&&(n=13<=parseInt(vi(n),10)?"11":"10"),this.set(m,s).set(v,n));break;case L:var n=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,n(V)).set(B,n(B)).set(F,n(F)).set($,n($)).set(D,n(D))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.1",zi.BROWSER=e([m,v,h]),zi.CPU=e([x]),zi.DEVICE=e([f,o,g,r,k,n,a,y,_]),zi.ENGINE=zi.OS=e([m,v]),typeof exports!==d?(typeof module!==d&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===c&&define.amd?define(function(){return zi}):typeof i!==d&&(i.UAParser=zi);var Ai,Oi=typeof i!==d&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?yi(i,S):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=L&&w.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,r=i[r].version;/not.a.brand/i.test(a)&&!/chromi/i.test(this.get(m))||this.set(m,xi(K+" ",a)).set(v,r).set(h,vi(r))}break;case B:var s=t[x];s&&(s&&"64"==t[A]&&(s+="64"),w.call(this.data,s+";",o));break;case F:t[k]&&this.set(g,k),t[f]&&this.set(f,t[f]);break;case D:s=t[N];s&&(n=t[z],s==ui&&(n=13<=parseInt(vi(n),10)?"11":"10"),this.set(m,s).set(v,n));break;case L:var n=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,n(V)).set(B,n(B)).set(F,n(F)).set($,n($)).set(D,n(D))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.1",zi.BROWSER=e([m,v,h]),zi.CPU=e([x]),zi.DEVICE=e([f,o,g,r,k,a,n,y,_]),zi.ENGINE=zi.OS=e([m,v]),typeof exports!==d?(typeof module!==d&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===c&&define.amd?define(function(){return zi}):typeof i!==d&&(i.UAParser=zi);var Ai,Oi=typeof i!==d&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/readme.md b/readme.md index ad229f322..fe1ec957b 100644 --- a/readme.md +++ b/readme.md @@ -477,7 +477,7 @@ http.createServer(function (req, res) { res.setHeader('Accept-CH', getHighEntropyValues); res.setHeader('Critical-CH', getHighEntropyValues); - var ua = uap(req.headers); + var ua = uap(req.headers).withClientHints(); // END since@2.0 */ diff --git a/src/ua-parser.js b/src/ua-parser.js index 6a82ed1ad..51cb0fa3c 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -932,26 +932,27 @@ ['data', createUAParserData(itemType, ua, rgxMap, uaCH)] ]); this.parse(); + var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua; switch(this.itemType) { case UA_BROWSER: // Brave-specific detection - if (NAVIGATOR && NAVIGATOR.userAgent == ua && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { this.set(NAME, 'Brave'); } this.set(MAJOR, majorize(this.get(VERSION))); break; case UA_DEVICE: - if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { this.set(TYPE, MOBILE); } // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { this.set(MODEL, 'iPad') .set(TYPE, TABLET); } break; case UA_OS: - if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { + if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); } break; diff --git a/src/ua-parser.mjs b/src/ua-parser.mjs index e0d93d0d8..4d5df1b29 100644 --- a/src/ua-parser.mjs +++ b/src/ua-parser.mjs @@ -936,26 +936,27 @@ const window = undefined; ['data', createUAParserData(itemType, ua, rgxMap, uaCH)] ]); this.parse(); + var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua; switch(this.itemType) { case UA_BROWSER: // Brave-specific detection - if (NAVIGATOR && NAVIGATOR.userAgent == ua && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { this.set(NAME, 'Brave'); } this.set(MAJOR, majorize(this.get(VERSION))); break; case UA_DEVICE: - if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { this.set(TYPE, MOBILE); } // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { this.set(MODEL, 'iPad') .set(TYPE, TABLET); } break; case UA_OS: - if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM] && NAVIGATOR_UADATA[PLATFORM] != 'Unknown') { + if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); } break; From 1e80cf3533559796e115d15875a0613fe619eb47 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 29 Mar 2023 23:42:30 +0700 Subject: [PATCH 062/388] Only set browser as Chromium if no other brand was found --- src/ua-parser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 51cb0fa3c..76d58192c 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -991,8 +991,8 @@ if (brands) { for (var i in brands) { var brandName = brands[i].brand, - brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) { + brandVersion = brands[i].version + if (!/not.a.brand/i.test(brandName) && i < 1 || /chromi/i.test(this.get(NAME))) { this.set(NAME, strip(GOOGLE+' ', brandName)) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); From 359cbecd32ec47f2f1e566310c34ef02d85d3dc2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Apr 2023 05:53:45 +0700 Subject: [PATCH 063/388] Fix #640 - Self-return thenable causing infinite-loop when awaited --- readme.md | 24 ++++++++++++++++++++---- src/ua-parser.js | 21 +++++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index fe1ec957b..999898593 100644 --- a/readme.md +++ b/readme.md @@ -287,9 +287,9 @@ engine.version; // "28.0.1500.95" engine.toString(); // "Blink 28.0.1500.95" ``` -#### * `withClientHints():Promise|Thenable` `since@2.0` +#### * `withClientHints():Promise|Thenable|object` `since@2.0` -Recently Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`. +Recently, Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`. ```js // client-side example @@ -311,7 +311,7 @@ Recently Chrome limits the information exposed through user-agent and introduces })(); ``` -Along with `User-Agent` HTTP header, Chrome also sends this client-hints data by default under `Sec-CH-UA-*` HTTP headers in each request. In server-side development, you can capture this extra information by passing the `req.headers` to `UAParser()` (see examples below). When using `withClientHints()` in nodejs environment and browser without client-hints support (basically anything that's not Chromium-based) the updated data will be returned as a `Thenable` (has `then()` method). +Along with `User-Agent` HTTP header, Chrome also sends this client-hints data by default under `Sec-CH-UA-*` HTTP headers in each request. In server-side development, you can capture this extra information by passing the `req.headers` to `UAParser()` (see examples below). When using `withClientHints()` in nodejs environment and browser without client-hints support (basically anything that's not Chromium-based), it will returns a new object with updated data. ```js // server-side example @@ -338,7 +338,10 @@ console.log(result2.os.name); // "Android" console.log(result2.device.type); // "mobile" console.log(result2.device.model); // "Galaxy S3 Marketing" -new UAParser(request.headers).getBrowser().withClientHints().then((browser) => { +new UAParser(request.headers) + .getBrowser() + .withClientHints() + .then((browser) => { console.log(browser.toString()); // Chrome 110.0.0.0 }); ``` @@ -378,6 +381,19 @@ let myParser2 = new UAParser({ console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"} ``` +Some basic extensions (although not very complete at the moment) can also be found under `ua-parser-js/extensions` submodule. + +```js +import { UAParser } from 'ua-parser-js'; +import { Emails } from 'ua-parser-js/extensions'; + +const browser = new UAParser(Emails) + .setUA('Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0') + .getBrowser(); + +console.log(browser.name); // Thunderbird +``` + # Usage diff --git a/src/ua-parser.js b/src/ua-parser.js index 76d58192c..b62138232 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -888,8 +888,21 @@ if (!NAVIGATOR_UADATA) { UAParserData.prototype.then = function (cb) { - cb(this); - return this; + var that = this; + var UAParserDataResolve = function () { + for (var prop in that) { + if (that.hasOwnProperty(prop)) { + this[prop] = that[prop]; + } + } + }; + UAParserDataResolve.prototype = { + is : UAParserData.prototype.is, + toString : UAParserData.prototype.toString + }; + var resolveData = new UAParserDataResolve(); + cb(resolveData); + return resolveData; }; } @@ -991,8 +1004,8 @@ if (brands) { for (var i in brands) { var brandName = brands[i].brand, - brandVersion = brands[i].version - if (!/not.a.brand/i.test(brandName) && i < 1 || /chromi/i.test(this.get(NAME))) { + brandVersion = brands[i].version; + if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { this.set(NAME, strip(GOOGLE+' ', brandName)) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); From 5d2acd8fe7e8029f09afebf9c8afb5bcf4bcd951 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Apr 2023 17:55:18 +0700 Subject: [PATCH 064/388] Bump version 2.0.0-alpha.2 --- bower.json | 2 +- changelog.md | 42 +++++++++++++++++---------- dist/ua-parser.min.js | 4 +-- dist/ua-parser.pack.js | 4 +-- package.js | 2 +- package.json | 2 +- src/enum/ua-parser-enum.js | 2 +- src/enum/ua-parser-enum.mjs | 2 +- src/extension/ua-parser-extension.js | 2 +- src/extension/ua-parser-extension.mjs | 2 +- src/ua-parser.js | 8 ++--- src/ua-parser.mjs | 27 ++++++++++++----- 12 files changed, 62 insertions(+), 37 deletions(-) diff --git a/bower.json b/bower.json index 05b8579f8..0f5d520fd 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ua-parser-js", - "version": "2.0.0-alpha.1", + "version": "2.0.0-alpha.2", "authors": [ "Faisal Salman " ], diff --git a/changelog.md b/changelog.md index a4aa6f1e3..76d4ef8ec 100644 --- a/changelog.md +++ b/changelog.md @@ -1,16 +1,32 @@ # UAParser.js Changelog -# Version 2.0.0-alpha.1 -- Breaking changes: - - Browser detection on mobile device: `Chrome => Mobile Chrome`, `Firefox => Mobile Firefox` - - OS detection: `Mac OS => macOS`, `Chromium OS => Chrome OS` -- Add some new methods in result object: - - Add support for client hints: `withClientHints()` - - Utility for easy comparison: `is()` - - Utility to print full-name: `toString()` -- Add support for ES module `import { UAParser } from 'ua-parser-js'` -- Provide Enums `'ua-parser-js/enums'` -- Provide Extensions `'ua-parser-js/extensions'` +# Version 2.0 +- What's breaking: + - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` + - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` +- What's new: + - Add some new methods in result object: + - Add support for client hints: `withClientHints()` + - Utility for easy comparison: `is()` + - Utility to print full-name: `toString()` + - Add support for ES module `import { UAParser } from 'ua-parser-js'` + - Provide Enums `'ua-parser-js/enums'` + - Provide Extensions `'ua-parser-js/extensions'` + +## Version 2.0.0-alpha.2 +- Fix browser result always returning Chromium when using `withClientHints()` +- Fix infinite-loop when await-ing `withClientHints()` in non-client-hints browser + +## Version 2.0.0-alpha.1 +- Initial work on new major version + + +# Version 0.7 / 1.0 + +Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. + +## Version 0.7.35 / 1.0.35 +- Fix result from user-supplied user-agent being altered - Add new browser: Heytap, TikTok - Add new engine: LibWeb - Add new OS: SerenityOS @@ -18,10 +34,6 @@ - Improve device detection: iPhone, Amazon Echo - Improve OS detection: iOS -# Version 0.7 / 1.0 - -Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. - ## Version 0.7.34 / 1.0.34 - Fix Sharp Mobile detected as Huawei Tablet - Fix IE8 bug diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 9bba36131..f928f0542 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-alpha.1 +/* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.1",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(isSelfNav&&!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)||/chromi/i.test(this.get(NAME))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;return this};function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(isSelfNav&&!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;return this};function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index ca1bc8f58..50850b5f9 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-alpha.1 +/* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman MIT License */ -!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?yi(i,S):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=L&&w.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,r=i[r].version;/not.a.brand/i.test(a)&&!/chromi/i.test(this.get(m))||this.set(m,xi(K+" ",a)).set(v,r).set(h,vi(r))}break;case B:var s=t[x];s&&(s&&"64"==t[A]&&(s+="64"),w.call(this.data,s+";",o));break;case F:t[k]&&this.set(g,k),t[f]&&this.set(f,t[f]);break;case D:s=t[N];s&&(n=t[z],s==ui&&(n=13<=parseInt(vi(n),10)?"11":"10"),this.set(m,s).set(v,n));break;case L:var n=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,n(V)).set(B,n(B)).set(F,n(F)).set($,n($)).set(D,n(D))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.1",zi.BROWSER=e([m,v,h]),zi.CPU=e([x]),zi.DEVICE=e([f,o,g,r,k,a,n,y,_]),zi.ENGINE=zi.OS=e([m,v]),typeof exports!==d?(typeof module!==d&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===c&&define.amd?define(function(){return zi}):typeof i!==d&&(i.UAParser=zi);var Ai,Oi=typeof i!==d&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,u){"use strict";function e(i){for(var e={},t=0;tT?yi(i,T):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=W&&l.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,s=i[r].version;!/not.a.brand/i.test(a)&&(r<1||/chromi/i.test(this.get(g)))&&this.set(g,xi(K+" ",a)).set(x,s).set(f,vi(s))}break;case B:var n=t[k];n&&(n&&"64"==t[A]&&(n+="64"),l.call(this.data,n+";",o));break;case $:t[y]&&this.set(v,y),t[m]&&this.set(m,t[m]);break;case L:n=t[N];n&&(w=t[z],n==ui&&(w=13<=parseInt(vi(w),10)?"11":"10"),this.set(g,n).set(x,w));break;case W:var w=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,w(V)).set(B,w(B)).set($,w($)).set(D,w(D)).set(L,w(L))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.2",zi.BROWSER=e([g,x,f]),zi.CPU=e([k]),zi.DEVICE=e([m,o,v,r,y,a,n,w,_]),zi.ENGINE=zi.OS=e([g,x]),typeof exports!==p?(typeof module!==p&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===d&&define.amd?define(function(){return zi}):typeof i!==p&&(i.UAParser=zi);var Ai,Oi=typeof i!==p&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package.js b/package.js index 3f2ddf1cb..cb7f57ac0 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'faisalman:ua-parser-js', - version: '2.0.0-alpha.1', + version: '2.0.0-alpha.2', summary: 'Lightweight JavaScript-based user-agent string parser', git: 'https://github.com/faisalman/ua-parser-js.git', documentation: 'readme.md' diff --git a/package.json b/package.json index 37191dcb7..8197b3fca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-alpha.1", + "version": "2.0.0-alpha.2", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enum/ua-parser-enum.js b/src/enum/ua-parser-enum.js index 8333eaf8f..9276b9ace 100644 --- a/src/enum/ua-parser-enum.js +++ b/src/enum/ua-parser-enum.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-alpha.1 +/* Enums for UAParser.js v2.0.0-alpha.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/enum/ua-parser-enum.mjs b/src/enum/ua-parser-enum.mjs index 7708532fa..07b791a94 100644 --- a/src/enum/ua-parser-enum.mjs +++ b/src/enum/ua-parser-enum.mjs @@ -3,7 +3,7 @@ // Source: /src/enum/ua-parser-enum.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-alpha.1 +/* Enums for UAParser.js v2.0.0-alpha.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/extension/ua-parser-extension.js b/src/extension/ua-parser-extension.js index dfa90ed50..8b1036636 100644 --- a/src/extension/ua-parser-extension.js +++ b/src/extension/ua-parser-extension.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-alpha.1 +/* Extensions for UAParser.js v2.0.0-alpha.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/extension/ua-parser-extension.mjs b/src/extension/ua-parser-extension.mjs index 8b645fc74..7a2f42a87 100644 --- a/src/extension/ua-parser-extension.mjs +++ b/src/extension/ua-parser-extension.mjs @@ -3,7 +3,7 @@ // Source: /src/extension/ua-parser-extension.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-alpha.1 +/* Extensions for UAParser.js v2.0.0-alpha.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/ua-parser.js b/src/ua-parser.js index b62138232..3799b0245 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-alpha.1 +/* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman MIT License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -17,7 +17,7 @@ ///////////// - var LIBVERSION = '2.0.0-alpha.1', + var LIBVERSION = '2.0.0-alpha.2', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -332,7 +332,7 @@ /miuibrowser\/([\w\.]+)/i // MIUI Browser ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [ /fxios\/([\w\.-]+)/i // Firefox for iOS - ], [VERSION, [NAME, PREFIX_MOBILE + 'Firefox']], [ + ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i @@ -391,7 +391,7 @@ // Gecko based /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile - ], [[NAME, PREFIX_MOBILE + 'Firefox'], VERSION], [ + ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality diff --git a/src/ua-parser.mjs b/src/ua-parser.mjs index 4d5df1b29..9b7dfc58a 100644 --- a/src/ua-parser.mjs +++ b/src/ua-parser.mjs @@ -5,7 +5,7 @@ const window = undefined; ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-alpha.1 +/* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman MIT License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,7 +21,7 @@ const window = undefined; ///////////// - var LIBVERSION = '2.0.0-alpha.1', + var LIBVERSION = '2.0.0-alpha.2', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -336,7 +336,7 @@ const window = undefined; /miuibrowser\/([\w\.]+)/i // MIUI Browser ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [ /fxios\/([\w\.-]+)/i // Firefox for iOS - ], [VERSION, [NAME, PREFIX_MOBILE + 'Firefox']], [ + ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i @@ -395,7 +395,7 @@ const window = undefined; // Gecko based /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile - ], [[NAME, PREFIX_MOBILE + 'Firefox'], VERSION], [ + ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality @@ -892,8 +892,21 @@ const window = undefined; if (!NAVIGATOR_UADATA) { UAParserData.prototype.then = function (cb) { - cb(this); - return this; + var that = this; + var UAParserDataResolve = function () { + for (var prop in that) { + if (that.hasOwnProperty(prop)) { + this[prop] = that[prop]; + } + } + }; + UAParserDataResolve.prototype = { + is : UAParserData.prototype.is, + toString : UAParserData.prototype.toString + }; + var resolveData = new UAParserDataResolve(); + cb(resolveData); + return resolveData; }; } @@ -996,7 +1009,7 @@ const window = undefined; for (var i in brands) { var brandName = brands[i].brand, brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) || /chromi/i.test(this.get(NAME))) { + if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { this.set(NAME, strip(GOOGLE+' ', brandName)) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); From 4711805a1c89c3705fd910b9d68af6b4ef561212 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 5 Apr 2023 22:28:35 +0700 Subject: [PATCH 065/388] Re-use previous result instead of re-parse it all over again --- src/ua-parser.js | 64 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 3799b0245..19b478085 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -846,10 +846,14 @@ assignFromEntries.call(this, init_props); } UAParserData.prototype.withClientHints = function () { + + var prevData = this; // nodejs / non-client-hints browsers if (!NAVIGATOR_UADATA) { - return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get(); + var item = new UAParserItem(itemType, ua, rgxMap, uaCH); + item.data = prevData; + return item.parseCH().get(); } // browsers based on chromium 85+ @@ -857,7 +861,9 @@ .getHighEntropyValues(CH_ALL_VALUES) .then(function (res) { var JS_UACH = new UAParserDataCH(res, false); - return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get(); + var item = new UAParserItem(itemType, ua, rgxMap, JS_UACH); + item.data = prevData; + return item.parseCH().get(); }); }; @@ -944,8 +950,29 @@ ['rgxMap', rgxMap], ['data', createUAParserData(itemType, ua, rgxMap, uaCH)] ]); - this.parse(); - var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua; + if(this.itemType == UA_RESULT) { + var createUAParserItem = function (itemType) { + return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseUA().get(); + }; + this.set('ua', ua) + .set(UA_BROWSER, createUAParserItem(UA_BROWSER)) + .set(UA_CPU, createUAParserItem(UA_CPU)) + .set(UA_DEVICE, createUAParserItem(UA_DEVICE)) + .set(UA_ENGINE, createUAParserItem(UA_ENGINE)) + .set(UA_OS, createUAParserItem(UA_OS)) + .get(); + } + return this; + } + UAParserItem.prototype.get = function (prop) { + if (!prop) return this.data; + return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; + }; + UAParserItem.prototype.parseUA = function () { + if (this.itemType != UA_RESULT) { + rgxMapper.call(this.data, this.ua, this.rgxMap); + } + var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == this.ua; switch(this.itemType) { case UA_BROWSER: // Brave-specific detection @@ -968,28 +995,6 @@ if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); } - break; - case UA_RESULT: - var createUAParserItem = function (itemType) { - return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get(); - }; - this.set('ua', ua) - .set(UA_BROWSER, createUAParserItem(UA_BROWSER)) - .set(UA_CPU, createUAParserItem(UA_CPU)) - .set(UA_DEVICE, createUAParserItem(UA_DEVICE)) - .set(UA_ENGINE, createUAParserItem(UA_ENGINE)) - .set(UA_OS, createUAParserItem(UA_OS)) - .get(); - } - return this; - } - UAParserItem.prototype.get = function (prop) { - if (!prop) return this.data; - return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; - }; - UAParserItem.prototype.parse = function () { - if (this.itemType != UA_RESULT) { - rgxMapper.call(this.data, this.ua, this.rgxMap); } return this; }; @@ -1038,8 +1043,11 @@ } break; case UA_RESULT: + var prevData = this.data; var createUAParserItemWithCH = function (itemType) { - return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get(); + var item = new UAParserItem(itemType, ua, rgxMap[itemType], uaCH); + item.data = prevData[itemType]; + return item.parseCH().get(); }; this.set('ua', ua) .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER)) @@ -1092,7 +1100,7 @@ createUAParserItemFunc = function (itemType) { return function () { - return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get(); + return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).parseUA().get(); }; }; From b385a73340f84c43b78d41e8147368c1fdba158b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 5 Apr 2023 23:29:27 +0700 Subject: [PATCH 066/388] Move feature detection to its own dedicated method --- src/ua-parser.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 19b478085..702bf7e9f 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -964,14 +964,7 @@ } return this; } - UAParserItem.prototype.get = function (prop) { - if (!prop) return this.data; - return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; - }; - UAParserItem.prototype.parseUA = function () { - if (this.itemType != UA_RESULT) { - rgxMapper.call(this.data, this.ua, this.rgxMap); - } + UAParserItem.prototype.detectFeature = function () { var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == this.ua; switch(this.itemType) { case UA_BROWSER: @@ -998,6 +991,17 @@ } return this; }; + UAParserItem.prototype.get = function (prop) { + if (!prop) return this.data; + return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; + }; + UAParserItem.prototype.parseUA = function () { + if (this.itemType != UA_RESULT) { + rgxMapper.call(this.data, this.ua, this.rgxMap); + } + this.detectFeature(); + return this; + }; UAParserItem.prototype.parseCH = function () { var ua = this.ua, uaCH = this.uaCH, From 59d8d836c2f326a753813b8a39ad079c55c6663c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Apr 2023 05:48:14 +0700 Subject: [PATCH 067/388] Clean up: remove travis, verup; move jshint config to inline --- .jshintrc | 3 --- .travis.yml | 18 ------------------ package.json | 19 ++++--------------- src/ua-parser.js | 4 +++- 4 files changed, 7 insertions(+), 37 deletions(-) delete mode 100644 .jshintrc delete mode 100644 .travis.yml diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 5798043ca..000000000 --- a/.jshintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "esversion": 3 -} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0f87fc37f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -arch: - - amd64 - - ppc64le -language: node_js -node_js: - - stable - - lts/* - -notifications: - email: false - -cache: - directories: - - node_modules - -sudo: false - -script: npm run test-ci \ No newline at end of file diff --git a/package.json b/package.json index 8197b3fca..cdd0bb2aa 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,10 @@ "name": "ua-parser-js", "version": "2.0.0-alpha.2", "author": "Faisal Salman (http://faisalman.com)", - "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment", + "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ "user-agent", + "client-hints", "parser", "browser", "engine", @@ -162,23 +163,12 @@ "src" ], "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"", + "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\/\\*jshint[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"", "test": "jshint src/ua-parser.js && mocha -R nyan test", "test-ci": "jshint src/ua-parser.js && mocha -R spec test", "verup": "node ./node_modules/verup", "version": "node ./node_modules/verup 0" }, - "verup": { - "files": [ - "bower.json", - "package.js", - "src/ua-parser.js" - ], - "regs": [ - "^((?:\\$|(\\s*\\*\\s*@)|(\\s*(?:var|,)?\\s+))(?:LIBVERSION|version)[\\s\\:='\"]+)([0-9]+(?:\\.[0-9]+){2,2})", - "^(\\/?\\s?\\*.*v)([0-9]+(?:\\.[0-9]+){2,2})" - ] - }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", @@ -186,8 +176,7 @@ "mocha": "~8.2.0", "requirejs": "^2.3.2", "safe-regex": "^2.1.1", - "uglify-js": "~3.12.0", - "verup": "^1.3.x" + "uglify-js": "~3.12.0" }, "repository": { "type": "git", diff --git a/src/ua-parser.js b/src/ua-parser.js index 702bf7e9f..8e6c89180 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -8,10 +8,12 @@ Source : https://github.com/faisalman/ua-parser-js */ ///////////////////////////////////////////////////////////////////////////////// +/*jshint esversion: 3 */ + (function (window, undefined) { 'use strict'; - + ////////////// // Constants ///////////// From e01663b48fa68680a795606dbbff1e2141ec68ab Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Apr 2023 04:40:59 +0700 Subject: [PATCH 068/388] Rearrange internal class & remove old Safari map --- src/ua-parser.js | 359 +++++++++++++++++++++-------------------- test/browser-test.json | 4 +- 2 files changed, 189 insertions(+), 174 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index 8e6c89180..ffa73eaa0 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -95,18 +95,7 @@ // Helper ////////// - var assignFromEntries = function (arr) { - for (var i in arr) { - var propName = arr[i]; - if (typeof propName == OBJ_TYPE && propName.length == 2) { - this[propName[0]] = propName[1]; - } else { - this[propName] = undefined; - } - } - return this; - }, - extend = function (regexes, extensions) { + var extend = function (regexes, extensions) { var mergedRegexes = {}; for (var i in regexes) { mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; @@ -150,6 +139,17 @@ majorize = function (version) { return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; }, + setProps = function (arr) { + for (var i in arr) { + var propName = arr[i]; + if (typeof propName == OBJ_TYPE && propName.length == 2) { + this[propName[0]] = propName[1]; + } else { + this[propName] = undefined; + } + } + return this; + }, strip = function (pattern, str) { return str.replace(pattern, EMPTY); }, @@ -243,18 +243,7 @@ // String map ////////////// - // Safari < 3.0 - var oldSafariMap = { - '1.0' : '/8', - '1.2' : '/1', - '1.3' : '/3', - '2.0' : '/412', - '2.0.2' : '/416', - '2.0.3' : '/417', - '2.0.4' : '/419', - '?' : '/' - }, - windowsVersionMap = { + var windowsVersionMap = { 'ME' : '4.90', 'NT 3.11' : 'NT3.51', 'NT 4.0' : 'NT4.0', @@ -386,7 +375,7 @@ /version\/([\w\.\,]+) .*(safari)/i // Safari ], [VERSION, NAME], [ /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 - ], [NAME, [VERSION, strMapper, oldSafariMap]], [ + ], [NAME, [VERSION, '1']], [ /(webkit|khtml)\/([\w\.]+)/i ], [NAME, VERSION], [ @@ -427,7 +416,7 @@ /((?:i[346]|x)86)[;\)]/i // IA32 (x86) ], [[ARCHITECTURE, 'ia32']], [ - /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 + /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 ], [[ARCHITECTURE, 'arm64']], [ /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF @@ -811,23 +800,23 @@ var defaultProps = (function () { var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; - assignFromEntries.call(props.init, [ + setProps.call(props.init, [ [UA_BROWSER, [NAME, VERSION, MAJOR]], [UA_CPU, [ARCHITECTURE]], [UA_DEVICE, [TYPE, MODEL, VENDOR]], [UA_ENGINE, [NAME, VERSION]], [UA_OS, [NAME, VERSION]] ]); - assignFromEntries.call(props.isIgnore, [ + setProps.call(props.isIgnore, [ [UA_BROWSER, [VERSION, MAJOR]], [UA_ENGINE, [VERSION]], [UA_OS, [VERSION]] ]); - assignFromEntries.call(props.isIgnoreRgx, [ + setProps.call(props.isIgnoreRgx, [ [UA_BROWSER, / ?browser$/i], [UA_OS, / ?os$/i] ]); - assignFromEntries.call(props.toString, [ + setProps.call(props.toString, [ [UA_BROWSER, [NAME, VERSION]], [UA_CPU, [ARCHITECTURE]], [UA_DEVICE, [VENDOR, MODEL]], @@ -837,7 +826,7 @@ return props; })(); - var createUAParserData = function (itemType, ua, rgxMap, uaCH) { + var createUAParserData = function (item, itemType) { var init_props = defaultProps.init[itemType], is_ignoreProps = defaultProps.isIgnore[itemType] || 0, @@ -845,27 +834,30 @@ toString_props = defaultProps.toString[itemType] || 0; function UAParserData () { - assignFromEntries.call(this, init_props); + setProps.call(this, init_props); } + + UAParserData.prototype.getItem = function () { + return item; + }; + UAParserData.prototype.withClientHints = function () { - var prevData = this; - // nodejs / non-client-hints browsers if (!NAVIGATOR_UADATA) { - var item = new UAParserItem(itemType, ua, rgxMap, uaCH); - item.data = prevData; - return item.parseCH().get(); + return item + .parseCH() + .get(); } // browsers based on chromium 85+ return NAVIGATOR_UADATA .getHighEntropyValues(CH_ALL_VALUES) .then(function (res) { - var JS_UACH = new UAParserDataCH(res, false); - var item = new UAParserItem(itemType, ua, rgxMap, JS_UACH); - item.data = prevData; - return item.parseCH().get(); + return item + .setCH(new UAParserDataCH(res, false)) + .parseCH() + .get(); }); }; @@ -923,9 +915,9 @@ function UAParserDataCH (uach, isHTTP_UACH) { uach = uach || {}; - assignFromEntries.call(this, CH_ALL_VALUES); + setProps.call(this, CH_ALL_VALUES); if (isHTTP_UACH) { - assignFromEntries.call(this, [ + setProps.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], [BRANDS, itemListToArray(uach[CH_HEADER])], @@ -941,133 +933,138 @@ if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop]; } } - return this; } function UAParserItem (itemType, ua, rgxMap, uaCH) { - assignFromEntries.call(this, [ + + this.get = function (prop) { + if (!prop) return this.data; + return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; + }; + + this.set = function (prop, val) { + this.data[prop] = val; + return this; + }; + + this.setCH = function (ch) { + this.uaCH = ch; + return this; + }; + + this.detectFeature = function () { + if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) { + switch (this.itemType) { + case UA_BROWSER: + // Brave-specific detection + if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); + } + break; + case UA_DEVICE: + // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); + } + break; + case UA_OS: + // Chrome-specific detection: check for 'platform' value of navigator.userAgentData + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { + this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); + } + } + } + return this; + }; + + this.parseUA = function () { + if (this.itemType != UA_RESULT) { + rgxMapper.call(this.data, this.ua, this.rgxMap); + } + if (this.itemType == UA_BROWSER) { + this.set(MAJOR, majorize(this.get(VERSION))); + } + return this; + }; + + this.parseCH = function () { + var ua = this.ua, + uaCH = this.uaCH, + rgxMap = this.rgxMap; + + switch (this.itemType) { + case UA_BROWSER: + var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; + if (brands) { + for (var i in brands) { + var brandName = brands[i].brand, + brandVersion = brands[i].version; + if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { + this.set(NAME, strip(GOOGLE+' ', brandName)) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + } + } + break; + case UA_CPU: + var archName = uaCH[ARCHITECTURE]; + if (archName) { + if (archName && uaCH[BITNESS] == '64') archName += '64'; + rgxMapper.call(this.data, archName + ';', rgxMap); + } + break; + case UA_DEVICE: + if (uaCH[MOBILE]) { + this.set(TYPE, MOBILE); + } + if (uaCH[MODEL]) { + this.set(MODEL, uaCH[MODEL]); + } + break; + case UA_OS: + var osName = uaCH[PLATFORM]; + if(osName) { + var osVersion = uaCH[PLATFORMVER]; + if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10'); + this.set(NAME, osName) + .set(VERSION, osVersion); + } + break; + case UA_RESULT: + var data = this.data; + var parse = function (itemType) { + return data[itemType] + .getItem() + .setCH(uaCH) + .parseCH() + .get(); + }; + this.set('ua', ua) + .set(UA_BROWSER, parse(UA_BROWSER)) + .set(UA_CPU, parse(UA_CPU)) + .set(UA_DEVICE, parse(UA_DEVICE)) + .set(UA_ENGINE, parse(UA_ENGINE)) + .set(UA_OS, parse(UA_OS)); + } + return this; + }; + + setProps.call(this, [ ['itemType', itemType], ['ua', ua], ['uaCH', uaCH], ['rgxMap', rgxMap], - ['data', createUAParserData(itemType, ua, rgxMap, uaCH)] + ['data', createUAParserData(this, itemType)] ]); - if(this.itemType == UA_RESULT) { - var createUAParserItem = function (itemType) { - return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseUA().get(); - }; - this.set('ua', ua) - .set(UA_BROWSER, createUAParserItem(UA_BROWSER)) - .set(UA_CPU, createUAParserItem(UA_CPU)) - .set(UA_DEVICE, createUAParserItem(UA_DEVICE)) - .set(UA_ENGINE, createUAParserItem(UA_ENGINE)) - .set(UA_OS, createUAParserItem(UA_OS)) - .get(); - } + return this; } - UAParserItem.prototype.detectFeature = function () { - var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == this.ua; - switch(this.itemType) { - case UA_BROWSER: - // Brave-specific detection - if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { - this.set(NAME, 'Brave'); - } - this.set(MAJOR, majorize(this.get(VERSION))); - break; - case UA_DEVICE: - if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { - this.set(TYPE, MOBILE); - } - // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { - this.set(MODEL, 'iPad') - .set(TYPE, TABLET); - } - break; - case UA_OS: - if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { - this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); - } - } - return this; - }; - UAParserItem.prototype.get = function (prop) { - if (!prop) return this.data; - return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; - }; - UAParserItem.prototype.parseUA = function () { - if (this.itemType != UA_RESULT) { - rgxMapper.call(this.data, this.ua, this.rgxMap); - } - this.detectFeature(); - return this; - }; - UAParserItem.prototype.parseCH = function () { - var ua = this.ua, - uaCH = this.uaCH, - rgxMap = this.rgxMap; - - switch (this.itemType) { - case UA_BROWSER: - var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; - if (brands) { - for (var i in brands) { - var brandName = brands[i].brand, - brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { - this.set(NAME, strip(GOOGLE+' ', brandName)) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - } - } - } - break; - case UA_CPU: - var archName = uaCH[ARCHITECTURE]; - if (archName) { - if (archName && uaCH[BITNESS] == '64') archName += '64'; - rgxMapper.call(this.data, archName + ';', rgxMap); - } - break; - case UA_DEVICE: - if (uaCH[MOBILE]) { - this.set(TYPE, MOBILE); - } - if (uaCH[MODEL]) { - this.set(MODEL, uaCH[MODEL]); - } - break; - case UA_OS: - var osName = uaCH[PLATFORM]; - if(osName) { - var osVersion = uaCH[PLATFORMVER]; - if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10'); - this.set(NAME, osName) - .set(VERSION, osVersion); - } - break; - case UA_RESULT: - var prevData = this.data; - var createUAParserItemWithCH = function (itemType) { - var item = new UAParserItem(itemType, ua, rgxMap[itemType], uaCH); - item.data = prevData[itemType]; - return item.parseCH().get(); - }; - this.set('ua', ua) - .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER)) - .set(UA_CPU, createUAParserItemWithCH(UA_CPU)) - .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE)) - .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE)) - .set(UA_OS, createUAParserItemWithCH(UA_OS)); - } - return this; - }; - UAParserItem.prototype.set = function (prop, val) { - this.data[prop] = val; - return this; - }; function UAParser (ua, extensions, headers) { @@ -1104,26 +1101,44 @@ extend(defaultRegexes, extensions) : defaultRegexes, - createUAParserItemFunc = function (itemType) { - return function () { - return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).parseUA().get(); - }; + createItemFunc = function (itemType) { + if (itemType == UA_RESULT) { + return function () { + return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH) + .set('ua', userAgent) + .set(UA_BROWSER, this.getBrowser()) + .set(UA_CPU, this.getCPU()) + .set(UA_DEVICE, this.getDevice()) + .set(UA_ENGINE, this.getEngine()) + .set(UA_OS, this.getOS()) + .get(); + }; + } else { + return function () { + return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) + .parseUA() + .detectFeature() + .get(); + }; + } }; // public methods - assignFromEntries.call(this, [ - ['getBrowser', createUAParserItemFunc(UA_BROWSER)], - ['getCPU', createUAParserItemFunc(UA_CPU)], - ['getDevice', createUAParserItemFunc(UA_DEVICE)], - ['getEngine', createUAParserItemFunc(UA_ENGINE)], - ['getOS', createUAParserItemFunc(UA_OS)], - ['getResult', createUAParserItemFunc(UA_RESULT)], + setProps.call(this, [ + ['getBrowser', createItemFunc(UA_BROWSER)], + ['getCPU', createItemFunc(UA_CPU)], + ['getDevice', createItemFunc(UA_DEVICE)], + ['getEngine', createItemFunc(UA_ENGINE)], + ['getOS', createItemFunc(UA_OS)], + ['getResult', createItemFunc(UA_RESULT)], ['getUA', function () { return userAgent; }], ['setUA', function (ua) { userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; return this; }] - ]).setUA(userAgent); + ]) + .setUA(userAgent); + return this; } diff --git a/test/browser-test.json b/test/browser-test.json index b8f7a6e80..9ca190c61 100644 --- a/test/browser-test.json +++ b/test/browser-test.json @@ -1084,8 +1084,8 @@ "expect" : { "name" : "Safari", - "version" : "2.0.4", - "major" : "2" + "version" : "1", + "major" : "1" } }, { From 625ece73e2d1adb3dca1c85defbc1a5069dd68de Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Apr 2023 06:44:30 +0700 Subject: [PATCH 069/388] Rearrange test files & config --- dist/ua-parser.min.js | 2 +- dist/ua-parser.pack.js | 2 +- package.json | 20 +- script/build-module.js | 39 ++ src/{enum => module}/ua-parser-enum.mjs | 6 +- .../ua-parser-extension.mjs | 6 +- src/{ => module}/ua-parser.mjs | 354 ++++++++++-------- src/{enum => }/ua-parser-enum.js | 4 +- src/{extension => }/ua-parser-extension.js | 4 +- src/ua-parser.js | 3 +- .../browser-all.json} | 0 test/{cpu-test.json => specs/cpu-all.json} | 0 .../device-all.json} | 0 .../engine-all.json} | 0 .../extension-device.json} | 0 .../extension-mediaplayer.json} | 0 test/{os-test.json => specs/os-all.json} | 0 test/test.js | 127 +------ 18 files changed, 262 insertions(+), 305 deletions(-) create mode 100644 script/build-module.js rename src/{enum => module}/ua-parser-enum.mjs (97%) rename src/{extension => module}/ua-parser-extension.mjs (98%) rename src/{ => module}/ua-parser.mjs (86%) rename src/{enum => }/ua-parser-enum.js (98%) rename src/{extension => }/ua-parser-extension.js (99%) rename test/{browser-test.json => specs/browser-all.json} (100%) rename test/{cpu-test.json => specs/cpu-all.json} (100%) rename test/{device-test.json => specs/device-all.json} (100%) rename test/{engine-test.json => specs/engine-all.json} (100%) rename test/{test-extension.json => specs/extension-device.json} (100%) rename test/{mediaplayer-test.json => specs/extension-mediaplayer.json} (100%) rename test/{os-test.json => specs/os-all.json} (100%) diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index f928f0542..d5b153cca 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var assignFromEntries=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(isSelfNav&&!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var createUAParserItem=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItem(UA_BROWSER)).set(UA_CPU,createUAParserItem(UA_CPU)).set(UA_DEVICE,createUAParserItem(UA_DEVICE)).set(UA_ENGINE,createUAParserItem(UA_ENGINE)).set(UA_OS,createUAParserItem(UA_OS)).get()}return this}UAParserItem.prototype.get=function(prop){if(!prop)return this.data;return this.data.hasOwnProperty(prop)?this.data[prop]:undefined};UAParserItem.prototype.parse=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}return this};UAParserItem.prototype.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var createUAParserItemWithCH=function(itemType){return new UAParserItem(itemType,ua,rgxMap[itemType],uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,createUAParserItemWithCH(UA_BROWSER)).set(UA_CPU,createUAParserItemWithCH(UA_CPU)).set(UA_DEVICE,createUAParserItemWithCH(UA_DEVICE)).set(UA_ENGINE,createUAParserItemWithCH(UA_ENGINE)).set(UA_OS,createUAParserItemWithCH(UA_OS))}return this};UAParserItem.prototype.set=function(prop,val){this.data[prop]=val;return this};function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createUAParserItemFunc=function(itemType){return function(){return new UAParserItem(itemType,userAgent,itemType==UA_RESULT?regexMap:regexMap[itemType],HTTP_UACH).get()}};assignFromEntries.call(this,[["getBrowser",createUAParserItemFunc(UA_BROWSER)],["getCPU",createUAParserItemFunc(UA_CPU)],["getDevice",createUAParserItemFunc(UA_DEVICE)],["getEngine",createUAParserItemFunc(UA_ENGINE)],["getOS",createUAParserItemFunc(UA_OS)],["getResult",createUAParserItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createUAParserData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAParserItem(itemType,userAgent,regexMap,HTTP_UACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAParserItem(itemType,userAgent,regexMap[itemType],HTTP_UACH).parseUA().detectFeature().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 50850b5f9..141057c29 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman MIT License */ -!function(i,u){"use strict";function e(i){for(var e={},t=0;tT?yi(i,T):i,this}]]).setUA(o),this}Ni.prototype.get=function(i){return i?this.data.hasOwnProperty(i)?this.data[i]:u:this.data},Ni.prototype.parse=function(){return this.itemType!=W&&l.call(this.data,this.ua,this.rgxMap),this},Ni.prototype.parseCH=function(){var e=this.ua,t=this.uaCH,o=this.rgxMap;switch(this.itemType){case V:var i=t[C]||t[q];if(i)for(var r in i){var a=i[r].brand,s=i[r].version;!/not.a.brand/i.test(a)&&(r<1||/chromi/i.test(this.get(g)))&&this.set(g,xi(K+" ",a)).set(x,s).set(f,vi(s))}break;case B:var n=t[k];n&&(n&&"64"==t[A]&&(n+="64"),l.call(this.data,n+";",o));break;case $:t[y]&&this.set(v,y),t[m]&&this.set(m,t[m]);break;case L:n=t[N];n&&(w=t[z],n==ui&&(w=13<=parseInt(vi(w),10)?"11":"10"),this.set(g,n).set(x,w));break;case W:var w=function(i){return new Ni(i,e,o[i],t).parseCH().get()};this.set("ua",e).set(V,w(V)).set(B,w(B)).set($,w($)).set(D,w(D)).set(L,w(L))}return this},Ni.prototype.set=function(i,e){return this.data[i]=e,this},zi.VERSION="2.0.0-alpha.2",zi.BROWSER=e([g,x,f]),zi.CPU=e([k]),zi.DEVICE=e([m,o,v,r,y,a,n,w,_]),zi.ENGINE=zi.OS=e([g,x]),typeof exports!==p?(typeof module!==p&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===d&&define.amd?define(function(){return zi}):typeof i!==p&&(i.UAParser=zi);var Ai,Oi=typeof i!==p&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ai=new zi,Oi.ua=Ai.getResult(),Oi.ua.get=function(){return Ai.getUA()},Oi.ua.set=function(i){Ai.setUA(i);var e,t=Ai.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i,this}]]).setUA(o),this}Ni.VERSION="2.0.0-alpha.2",Ni.BROWSER=e([h,m,l]),Ni.CPU=e([g]),Ni.DEVICE=e([p,o,f,r,v,x,a,k,y]),Ni.ENGINE=Ni.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ni),exports.UAParser=Ni):typeof define===c&&define.amd?define(function(){return Ni}):typeof i!==b&&(i.UAParser=Ni);var Oi,zi=typeof i!==b&&(i.jQuery||i.Zepto);zi&&!zi.ua&&(Oi=new Ni,zi.ua=Oi.getResult(),zi.ua.get=function(){return Oi.getUA()},zi.ua.set=function(i){Oi.setUA(i);var e,t=Oi.getResult();for(e in t)zi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package.json b/package.json index cdd0bb2aa..259988cca 100644 --- a/package.json +++ b/package.json @@ -143,19 +143,19 @@ ], "type": "commonjs", "main": "src/ua-parser.js", - "module": "src/ua-parser.mjs", + "module": "src/module/ua-parser.mjs", "exports": { ".": { "require": "./src/ua-parser.js", - "import": "./src/ua-parser.mjs" + "import": "./src/module/ua-parser.mjs" }, "./enums": { - "require": "./src/enum/ua-parser-enum.js", - "import": "./src/enum/ua-parser-enum.mjs" + "require": "./src/ua-parser-enum.js", + "import": "./src/module/ua-parser-enum.mjs" }, "./extensions": { - "require": "./src/extension/ua-parser-extension.js", - "import": "./src/extension/ua-parser-extension.mjs" + "require": "./src/ua-parser-extension.js", + "import": "./src/module/ua-parser-extension.mjs" } }, "files": [ @@ -163,11 +163,9 @@ "src" ], "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\/\\*jshint[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"", - "test": "jshint src/ua-parser.js && mocha -R nyan test", - "test-ci": "jshint src/ua-parser.js && mocha -R spec test", - "verup": "node ./node_modules/verup", - "version": "node ./node_modules/verup 0" + "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node script/build-module.js", + "test": "jshint src/ && mocha -R nyan test", + "test-ci": "jshint src/ && mocha -R spec test" }, "devDependencies": { "@babel/parser": "7.15.8", diff --git a/script/build-module.js b/script/build-module.js new file mode 100644 index 000000000..e002e55c7 --- /dev/null +++ b/script/build-module.js @@ -0,0 +1,39 @@ +const fs = require('fs'); + +/*/////////////// +// ua-parser.mjs +//////////////*/ + +fs.writeFileSync('src/module/ua-parser.mjs', +`// Generated ESM version of UAParser.js +// DO NOT EDIT THIS FILE! +// Source: /src/ua-parser.js + +` + fs.readFileSync('src/ua-parser.js','utf-8').replace(/\(func[\s\S]+strict\';/ig,'') + .replace(/esversion\: 3/ig, 'esversion: 6') + .replace(/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'),'utf-8'); + +/*///////////////////// +// ua-parser-enum.mjs +////////////////////*/ + +fs.writeFileSync('src/module/ua-parser-enum.mjs', +`// Generated ESM version of UAParser.js enums +// DO NOT EDIT THIS FILE! +// Source: /src/ua-parser-enum.js + +` + fs.readFileSync('src/ua-parser-enum.js','utf-8') + .replace(/module\.exports =/ig,'export'),'utf-8'); + +/*////////////////////////// +// ua-parser-extension.mjs +/////////////////////////*/ + +fs.writeFileSync('src/module/ua-parser-extension.mjs', +`// Generated ESM version of UAParser.js extensions +// DO NOT EDIT THIS FILE! +// Source: /src/ua-parser-extension.js + +` + fs.readFileSync('src/ua-parser-extension.js','utf-8') + .replace(/const UA.+\)/ig,'import UAParser from \'ua-parser-js\'') + .replace(/module\.exports =/ig,'export'),'utf-8'); \ No newline at end of file diff --git a/src/enum/ua-parser-enum.mjs b/src/module/ua-parser-enum.mjs similarity index 97% rename from src/enum/ua-parser-enum.mjs rename to src/module/ua-parser-enum.mjs index 07b791a94..2081f10f5 100644 --- a/src/enum/ua-parser-enum.mjs +++ b/src/module/ua-parser-enum.mjs @@ -1,6 +1,6 @@ // Generated ESM version of UAParser.js enums // DO NOT EDIT THIS FILE! -// Source: /src/enum/ua-parser-enum.js +// Source: /src/ua-parser-enum.js /////////////////////////////////////////////// /* Enums for UAParser.js v2.0.0-alpha.2 @@ -9,6 +9,8 @@ MIT License */ ////////////////////////////////////////////// +/*jshint esversion: 6 */ + const BrowserName = Object.freeze({ CHROME : 'Chrome', EDGE : 'Edge', @@ -102,4 +104,4 @@ export { DeviceVendor, EngineName, OSName -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/extension/ua-parser-extension.mjs b/src/module/ua-parser-extension.mjs similarity index 98% rename from src/extension/ua-parser-extension.mjs rename to src/module/ua-parser-extension.mjs index 7a2f42a87..44932a2c9 100644 --- a/src/extension/ua-parser-extension.mjs +++ b/src/module/ua-parser-extension.mjs @@ -1,6 +1,6 @@ // Generated ESM version of UAParser.js extensions // DO NOT EDIT THIS FILE! -// Source: /src/extension/ua-parser-extension.js +// Source: /src/ua-parser-extension.js /////////////////////////////////////////////// /* Extensions for UAParser.js v2.0.0-alpha.2 @@ -9,6 +9,8 @@ MIT License */ ////////////////////////////////////////////// +/*jshint esversion: 6 */ + const MODEL = 'model'; const NAME = 'name'; const TYPE = 'type'; @@ -121,4 +123,4 @@ export { ExtraDevices, Emails, Tools -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/ua-parser.mjs b/src/module/ua-parser.mjs similarity index 86% rename from src/ua-parser.mjs rename to src/module/ua-parser.mjs index 9b7dfc58a..ba554e354 100644 --- a/src/ua-parser.mjs +++ b/src/module/ua-parser.mjs @@ -2,8 +2,6 @@ // DO NOT EDIT THIS FILE! // Source: /src/ua-parser.js -const window = undefined; - ///////////////////////////////////////////////////////////////////////////////// /* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman @@ -14,8 +12,11 @@ const window = undefined; Source : https://github.com/faisalman/ua-parser-js */ ///////////////////////////////////////////////////////////////////////////////// +/* jshint esversion: 6 */ +/* globals window */ + ////////////// // Constants ///////////// @@ -97,18 +98,7 @@ const window = undefined; // Helper ////////// - var assignFromEntries = function (arr) { - for (var i in arr) { - var propName = arr[i]; - if (typeof propName == OBJ_TYPE && propName.length == 2) { - this[propName[0]] = propName[1]; - } else { - this[propName] = undefined; - } - } - return this; - }, - extend = function (regexes, extensions) { + var extend = function (regexes, extensions) { var mergedRegexes = {}; for (var i in regexes) { mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; @@ -152,6 +142,17 @@ const window = undefined; majorize = function (version) { return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; }, + setProps = function (arr) { + for (var i in arr) { + var propName = arr[i]; + if (typeof propName == OBJ_TYPE && propName.length == 2) { + this[propName[0]] = propName[1]; + } else { + this[propName] = undefined; + } + } + return this; + }, strip = function (pattern, str) { return str.replace(pattern, EMPTY); }, @@ -245,18 +246,7 @@ const window = undefined; // String map ////////////// - // Safari < 3.0 - var oldSafariMap = { - '1.0' : '/8', - '1.2' : '/1', - '1.3' : '/3', - '2.0' : '/412', - '2.0.2' : '/416', - '2.0.3' : '/417', - '2.0.4' : '/419', - '?' : '/' - }, - windowsVersionMap = { + var windowsVersionMap = { 'ME' : '4.90', 'NT 3.11' : 'NT3.51', 'NT 4.0' : 'NT4.0', @@ -388,7 +378,7 @@ const window = undefined; /version\/([\w\.\,]+) .*(safari)/i // Safari ], [VERSION, NAME], [ /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 - ], [NAME, [VERSION, strMapper, oldSafariMap]], [ + ], [NAME, [VERSION, '1']], [ /(webkit|khtml)\/([\w\.]+)/i ], [NAME, VERSION], [ @@ -429,7 +419,7 @@ const window = undefined; /((?:i[346]|x)86)[;\)]/i // IA32 (x86) ], [[ARCHITECTURE, 'ia32']], [ - /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 + /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 ], [[ARCHITECTURE, 'arm64']], [ /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF @@ -813,23 +803,23 @@ const window = undefined; var defaultProps = (function () { var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; - assignFromEntries.call(props.init, [ + setProps.call(props.init, [ [UA_BROWSER, [NAME, VERSION, MAJOR]], [UA_CPU, [ARCHITECTURE]], [UA_DEVICE, [TYPE, MODEL, VENDOR]], [UA_ENGINE, [NAME, VERSION]], [UA_OS, [NAME, VERSION]] ]); - assignFromEntries.call(props.isIgnore, [ + setProps.call(props.isIgnore, [ [UA_BROWSER, [VERSION, MAJOR]], [UA_ENGINE, [VERSION]], [UA_OS, [VERSION]] ]); - assignFromEntries.call(props.isIgnoreRgx, [ + setProps.call(props.isIgnoreRgx, [ [UA_BROWSER, / ?browser$/i], [UA_OS, / ?os$/i] ]); - assignFromEntries.call(props.toString, [ + setProps.call(props.toString, [ [UA_BROWSER, [NAME, VERSION]], [UA_CPU, [ARCHITECTURE]], [UA_DEVICE, [VENDOR, MODEL]], @@ -839,7 +829,7 @@ const window = undefined; return props; })(); - var createUAParserData = function (itemType, ua, rgxMap, uaCH) { + var createUAParserData = function (item, itemType) { var init_props = defaultProps.init[itemType], is_ignoreProps = defaultProps.isIgnore[itemType] || 0, @@ -847,21 +837,30 @@ const window = undefined; toString_props = defaultProps.toString[itemType] || 0; function UAParserData () { - assignFromEntries.call(this, init_props); + setProps.call(this, init_props); } + + UAParserData.prototype.getItem = function () { + return item; + }; + UAParserData.prototype.withClientHints = function () { - + // nodejs / non-client-hints browsers if (!NAVIGATOR_UADATA) { - return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get(); + return item + .parseCH() + .get(); } // browsers based on chromium 85+ return NAVIGATOR_UADATA .getHighEntropyValues(CH_ALL_VALUES) .then(function (res) { - var JS_UACH = new UAParserDataCH(res, false); - return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get(); + return item + .setCH(new UAParserDataCH(res, false)) + .parseCH() + .get(); }); }; @@ -919,9 +918,9 @@ const window = undefined; function UAParserDataCH (uach, isHTTP_UACH) { uach = uach || {}; - assignFromEntries.call(this, CH_ALL_VALUES); + setProps.call(this, CH_ALL_VALUES); if (isHTTP_UACH) { - assignFromEntries.call(this, [ + setProps.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], [BRANDS, itemListToArray(uach[CH_HEADER])], @@ -937,127 +936,138 @@ const window = undefined; if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop]; } } - return this; } function UAParserItem (itemType, ua, rgxMap, uaCH) { - assignFromEntries.call(this, [ + + this.get = function (prop) { + if (!prop) return this.data; + return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; + }; + + this.set = function (prop, val) { + this.data[prop] = val; + return this; + }; + + this.setCH = function (ch) { + this.uaCH = ch; + return this; + }; + + this.detectFeature = function () { + if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) { + switch (this.itemType) { + case UA_BROWSER: + // Brave-specific detection + if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); + } + break; + case UA_DEVICE: + // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); + } + break; + case UA_OS: + // Chrome-specific detection: check for 'platform' value of navigator.userAgentData + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { + this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); + } + } + } + return this; + }; + + this.parseUA = function () { + if (this.itemType != UA_RESULT) { + rgxMapper.call(this.data, this.ua, this.rgxMap); + } + if (this.itemType == UA_BROWSER) { + this.set(MAJOR, majorize(this.get(VERSION))); + } + return this; + }; + + this.parseCH = function () { + var ua = this.ua, + uaCH = this.uaCH, + rgxMap = this.rgxMap; + + switch (this.itemType) { + case UA_BROWSER: + var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; + if (brands) { + for (var i in brands) { + var brandName = brands[i].brand, + brandVersion = brands[i].version; + if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { + this.set(NAME, strip(GOOGLE+' ', brandName)) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + } + } + break; + case UA_CPU: + var archName = uaCH[ARCHITECTURE]; + if (archName) { + if (archName && uaCH[BITNESS] == '64') archName += '64'; + rgxMapper.call(this.data, archName + ';', rgxMap); + } + break; + case UA_DEVICE: + if (uaCH[MOBILE]) { + this.set(TYPE, MOBILE); + } + if (uaCH[MODEL]) { + this.set(MODEL, uaCH[MODEL]); + } + break; + case UA_OS: + var osName = uaCH[PLATFORM]; + if(osName) { + var osVersion = uaCH[PLATFORMVER]; + if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10'); + this.set(NAME, osName) + .set(VERSION, osVersion); + } + break; + case UA_RESULT: + var data = this.data; + var parse = function (itemType) { + return data[itemType] + .getItem() + .setCH(uaCH) + .parseCH() + .get(); + }; + this.set('ua', ua) + .set(UA_BROWSER, parse(UA_BROWSER)) + .set(UA_CPU, parse(UA_CPU)) + .set(UA_DEVICE, parse(UA_DEVICE)) + .set(UA_ENGINE, parse(UA_ENGINE)) + .set(UA_OS, parse(UA_OS)); + } + return this; + }; + + setProps.call(this, [ ['itemType', itemType], ['ua', ua], ['uaCH', uaCH], ['rgxMap', rgxMap], - ['data', createUAParserData(itemType, ua, rgxMap, uaCH)] + ['data', createUAParserData(this, itemType)] ]); - this.parse(); - var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua; - switch(this.itemType) { - case UA_BROWSER: - // Brave-specific detection - if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { - this.set(NAME, 'Brave'); - } - this.set(MAJOR, majorize(this.get(VERSION))); - break; - case UA_DEVICE: - if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { - this.set(TYPE, MOBILE); - } - // iPadOS-specific detection: identified as Mac, but has some iOS-only properties - if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { - this.set(MODEL, 'iPad') - .set(TYPE, TABLET); - } - break; - case UA_OS: - if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { - this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); - } - break; - case UA_RESULT: - var createUAParserItem = function (itemType) { - return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get(); - }; - this.set('ua', ua) - .set(UA_BROWSER, createUAParserItem(UA_BROWSER)) - .set(UA_CPU, createUAParserItem(UA_CPU)) - .set(UA_DEVICE, createUAParserItem(UA_DEVICE)) - .set(UA_ENGINE, createUAParserItem(UA_ENGINE)) - .set(UA_OS, createUAParserItem(UA_OS)) - .get(); - } + return this; } - UAParserItem.prototype.get = function (prop) { - if (!prop) return this.data; - return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; - }; - UAParserItem.prototype.parse = function () { - if (this.itemType != UA_RESULT) { - rgxMapper.call(this.data, this.ua, this.rgxMap); - } - return this; - }; - UAParserItem.prototype.parseCH = function () { - var ua = this.ua, - uaCH = this.uaCH, - rgxMap = this.rgxMap; - - switch (this.itemType) { - case UA_BROWSER: - var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; - if (brands) { - for (var i in brands) { - var brandName = brands[i].brand, - brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { - this.set(NAME, strip(GOOGLE+' ', brandName)) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - } - } - } - break; - case UA_CPU: - var archName = uaCH[ARCHITECTURE]; - if (archName) { - if (archName && uaCH[BITNESS] == '64') archName += '64'; - rgxMapper.call(this.data, archName + ';', rgxMap); - } - break; - case UA_DEVICE: - if (uaCH[MOBILE]) { - this.set(TYPE, MOBILE); - } - if (uaCH[MODEL]) { - this.set(MODEL, uaCH[MODEL]); - } - break; - case UA_OS: - var osName = uaCH[PLATFORM]; - if(osName) { - var osVersion = uaCH[PLATFORMVER]; - if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10'); - this.set(NAME, osName) - .set(VERSION, osVersion); - } - break; - case UA_RESULT: - var createUAParserItemWithCH = function (itemType) { - return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get(); - }; - this.set('ua', ua) - .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER)) - .set(UA_CPU, createUAParserItemWithCH(UA_CPU)) - .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE)) - .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE)) - .set(UA_OS, createUAParserItemWithCH(UA_OS)); - } - return this; - }; - UAParserItem.prototype.set = function (prop, val) { - this.data[prop] = val; - return this; - }; function UAParser (ua, extensions, headers) { @@ -1094,26 +1104,44 @@ const window = undefined; extend(defaultRegexes, extensions) : defaultRegexes, - createUAParserItemFunc = function (itemType) { - return function () { - return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get(); - }; + createItemFunc = function (itemType) { + if (itemType == UA_RESULT) { + return function () { + return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH) + .set('ua', userAgent) + .set(UA_BROWSER, this.getBrowser()) + .set(UA_CPU, this.getCPU()) + .set(UA_DEVICE, this.getDevice()) + .set(UA_ENGINE, this.getEngine()) + .set(UA_OS, this.getOS()) + .get(); + }; + } else { + return function () { + return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) + .parseUA() + .detectFeature() + .get(); + }; + } }; // public methods - assignFromEntries.call(this, [ - ['getBrowser', createUAParserItemFunc(UA_BROWSER)], - ['getCPU', createUAParserItemFunc(UA_CPU)], - ['getDevice', createUAParserItemFunc(UA_DEVICE)], - ['getEngine', createUAParserItemFunc(UA_ENGINE)], - ['getOS', createUAParserItemFunc(UA_OS)], - ['getResult', createUAParserItemFunc(UA_RESULT)], + setProps.call(this, [ + ['getBrowser', createItemFunc(UA_BROWSER)], + ['getCPU', createItemFunc(UA_CPU)], + ['getDevice', createItemFunc(UA_DEVICE)], + ['getEngine', createItemFunc(UA_ENGINE)], + ['getOS', createItemFunc(UA_OS)], + ['getResult', createItemFunc(UA_RESULT)], ['getUA', function () { return userAgent; }], ['setUA', function (ua) { userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; return this; }] - ]).setUA(userAgent); + ]) + .setUA(userAgent); + return this; } diff --git a/src/enum/ua-parser-enum.js b/src/ua-parser-enum.js similarity index 98% rename from src/enum/ua-parser-enum.js rename to src/ua-parser-enum.js index 9276b9ace..6d88e813f 100644 --- a/src/enum/ua-parser-enum.js +++ b/src/ua-parser-enum.js @@ -5,6 +5,8 @@ MIT License */ ////////////////////////////////////////////// +/*jshint esversion: 6 */ + const BrowserName = Object.freeze({ CHROME : 'Chrome', EDGE : 'Edge', @@ -98,4 +100,4 @@ module.exports = { DeviceVendor, EngineName, OSName -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/extension/ua-parser-extension.js b/src/ua-parser-extension.js similarity index 99% rename from src/extension/ua-parser-extension.js rename to src/ua-parser-extension.js index 8b1036636..fed987eb3 100644 --- a/src/extension/ua-parser-extension.js +++ b/src/ua-parser-extension.js @@ -5,6 +5,8 @@ MIT License */ ////////////////////////////////////////////// +/*jshint esversion: 6 */ + const MODEL = 'model'; const NAME = 'name'; const TYPE = 'type'; @@ -117,4 +119,4 @@ module.exports = { ExtraDevices, Emails, Tools -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/ua-parser.js b/src/ua-parser.js index ffa73eaa0..1e04b5940 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -8,7 +8,8 @@ Source : https://github.com/faisalman/ua-parser-js */ ///////////////////////////////////////////////////////////////////////////////// -/*jshint esversion: 3 */ +/* jshint esversion: 3 */ +/* globals window */ (function (window, undefined) { diff --git a/test/browser-test.json b/test/specs/browser-all.json similarity index 100% rename from test/browser-test.json rename to test/specs/browser-all.json diff --git a/test/cpu-test.json b/test/specs/cpu-all.json similarity index 100% rename from test/cpu-test.json rename to test/specs/cpu-all.json diff --git a/test/device-test.json b/test/specs/device-all.json similarity index 100% rename from test/device-test.json rename to test/specs/device-all.json diff --git a/test/engine-test.json b/test/specs/engine-all.json similarity index 100% rename from test/engine-test.json rename to test/specs/engine-all.json diff --git a/test/test-extension.json b/test/specs/extension-device.json similarity index 100% rename from test/test-extension.json rename to test/specs/extension-device.json diff --git a/test/mediaplayer-test.json b/test/specs/extension-mediaplayer.json similarity index 100% rename from test/mediaplayer-test.json rename to test/specs/extension-mediaplayer.json diff --git a/test/os-test.json b/test/specs/os-all.json similarity index 100% rename from test/os-test.json rename to test/specs/os-all.json diff --git a/test/test.js b/test/test.js index a7dbeea43..c998088b7 100644 --- a/test/test.js +++ b/test/test.js @@ -5,11 +5,11 @@ var requirejs = require('requirejs'); var parseJS = require('@babel/parser').parse; var traverse = require('@babel/traverse').default; var UAParser = require('./../src/ua-parser'); -var browsers = require('./browser-test.json'); -var cpus = require('./cpu-test.json'); -var devices = require('./device-test.json'); -var engines = require('./engine-test.json'); -var os = require('./os-test.json'); +var browsers = require('./specs/browser-all.json'); +var cpus = require('./specs/cpu-all.json'); +var devices = require('./specs/device-all.json'); +var engines = require('./specs/engine-all.json'); +var os = require('./specs/os-all.json'); var parser = new UAParser(); var methods = [ { @@ -82,7 +82,6 @@ describe('Returns', function () { assert.deepEqual(new UAParser('').getResult(), { ua : '', - //ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, browser: { name: undefined, version: undefined, major: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, @@ -362,43 +361,6 @@ describe('Map UA-CH headers', function () { let engine = new UAParser(headers).getEngine().withClientHints(); let os = new UAParser(headers).getOS().withClientHints(); -/* let ua_ch = { - "architecture": "ARM", - "bitness": "64", - "brands": [ - { - "brand": "Chromium", - "version": "93" - }, - { - "brand": "Google Chrome", - "version": "93" - }, - { - "brand": " Not;A Brand", - "version": "99" - } - ], - "fullVersionList": [ - { - "brand": "Chromium", - "version": "93.0.1.2" - }, - { - "brand": "Google Chrome", - "version": "93.0.1.2" - }, - { - "brand": " Not;A Brand", - "version": "99.0.1.2" - } - ], - "mobile": true, - "model": "Pixel 99", - "platform": "Windows", - "platformVersion": "13" - };*/ - it('Can read from client-hints headers using `withClientHints()`', function () { //assert.deepEqual(uap.ua_ch, ua_ch); @@ -436,7 +398,6 @@ describe('Map UA-CH headers', function () { engine = new UAParser(headers).getEngine(); os = new UAParser(headers).getOS(); - //assert.deepEqual(uap.ua_ch, ua_ch); assert.strictEqual(uap.browser.name, "Chrome"); assert.strictEqual(uap.browser.version, "110.0.0.0"); assert.strictEqual(uap.browser.major, "110"); @@ -459,18 +420,6 @@ describe('Map UA-CH headers', function () { uap = UAParser(headers2).withClientHints(); -/* ua_ch = { - "architecture": undefined, - "bitness": undefined, - "brands": undefined, - "fullVersionList": undefined, - "mobile": true, - "model": undefined, - "platform": undefined, - "platformVersion": undefined - }; - - assert.deepEqual(uap.ua_ch, ua_ch);*/ assert.strictEqual(uap.browser.name, "Chrome"); assert.strictEqual(uap.browser.version, "110.0.0.0"); assert.strictEqual(uap.browser.major, "110"); @@ -513,70 +462,4 @@ describe('Map UA-CH headers', function () { assert.strictEqual(ua.device.is("tablet"), false); }); }); -}); - -describe('Map UA-CH JS', () => { - - it('does not throw when using withClientHints() in non-supported environment', () => { - assert.doesNotThrow(() => { - new UAParser().getResult().withClientHints(); - }); - }); - - window = { navigator : { userAgent : '' , userAgentData : new NavigatorUAData() } }; - function NavigatorUAData () { - this.mobile = false; - this.platform = ''; - this.brands = [{ brand : 'A Chromium-based Browser', version : '1' }]; - this.getHighEntropyValues = (values) => { - return new Promise((resolve) => { - const result = { - brands : [{ brand : 'A Chromium-based Browser', version : '1' }], - fullVersionList : [{ brand : 'A Chromium-based Browser', version : '1.2.3' }], - architecture : 'x86', - bitness : '64', - mobile : true, - model : 'Galaxy S3', - platform : 'Android', - platformVersion : '1000' - }; - resolve(result); - }); - } - }; - delete require.cache[require.resolve('./../src/ua-parser')]; - const UAParserWithWindow = require('./../src/ua-parser'); - - it('Can read client hints from browser', async () => { - - let uap = new UAParserWithWindow() - - let os = await uap.getOS().withClientHints(); - - assert.strictEqual(os.name, 'Android'); - assert.strictEqual(os.is('Android'), true); - assert.strictEqual(os.toString(), 'Android 1000'); - - let result = await uap.getResult().withClientHints(); - - assert.strictEqual(result.browser.name, 'A Chromium-based Browser'); - assert.strictEqual(result.browser.version, '1.2.3'); - assert.strictEqual(result.cpu.architecture, 'amd64'); - assert.strictEqual(result.os.name, 'Android'); - - await uap.getDevice().withClientHints().then((device) => { - assert.strictEqual(device.type, 'mobile'); - assert.strictEqual(device.vendor, undefined); - assert.strictEqual(device.model, 'Galaxy S3'); - }); - - let result_without_ch = uap.getResult(); - - assert.strictEqual(result_without_ch.browser.name, undefined); - - uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); - assert.strictEqual(uap.getOS().name, "macOS"); - - // TODO : create full tests - }); }); \ No newline at end of file From 05747dba370731a56959935295679ccf1f6e76f2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Apr 2023 09:26:07 +0700 Subject: [PATCH 070/388] Install `@playwright/test` to perform test in real browser --- .gitignore | 1 + package.json | 5 ++- script/build-module.js | 1 + test/{test-es6.mjs => mocha-test-es6.mjs} | 0 ...t-extension.js => mocha-test-extension.js} | 0 test/{test.js => mocha-test.js} | 2 +- test/playwright-test-browser.spec.mjs | 44 +++++++++++++++++++ 7 files changed, 50 insertions(+), 3 deletions(-) rename test/{test-es6.mjs => mocha-test-es6.mjs} (100%) rename test/{test-extension.js => mocha-test-extension.js} (100%) rename test/{test.js => mocha-test.js} (99%) create mode 100644 test/playwright-test-browser.spec.mjs diff --git a/.gitignore b/.gitignore index ca8c34e70..0cd892f4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ npm-debug.log +playwright-report/ ### vim ### .*.s[a-w][a-z] diff --git a/package.json b/package.json index 259988cca..faba6cc57 100644 --- a/package.json +++ b/package.json @@ -164,12 +164,13 @@ ], "scripts": { "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node script/build-module.js", - "test": "jshint src/ && mocha -R nyan test", - "test-ci": "jshint src/ && mocha -R spec test" + "test": "jshint src && mocha -R nyan test/mocha*js && npx playwright test", + "test-ci": "jshint src && mocha -R spec test/mocha*js && npx playwright test" }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", + "@playwright/test": "^1.32.2", "jshint": "~2.12.0", "mocha": "~8.2.0", "requirejs": "^2.3.2", diff --git a/script/build-module.js b/script/build-module.js index e002e55c7..5b47349dc 100644 --- a/script/build-module.js +++ b/script/build-module.js @@ -1,3 +1,4 @@ +#!/usr/bin/env node const fs = require('fs'); /*/////////////// diff --git a/test/test-es6.mjs b/test/mocha-test-es6.mjs similarity index 100% rename from test/test-es6.mjs rename to test/mocha-test-es6.mjs diff --git a/test/test-extension.js b/test/mocha-test-extension.js similarity index 100% rename from test/test-extension.js rename to test/mocha-test-extension.js diff --git a/test/test.js b/test/mocha-test.js similarity index 99% rename from test/test.js rename to test/mocha-test.js index c998088b7..e85322dd6 100644 --- a/test/test.js +++ b/test/mocha-test.js @@ -4,7 +4,7 @@ var assert = require('assert'); var requirejs = require('requirejs'); var parseJS = require('@babel/parser').parse; var traverse = require('@babel/traverse').default; -var UAParser = require('./../src/ua-parser'); +var UAParser = require('../src/ua-parser'); var browsers = require('./specs/browser-all.json'); var cpus = require('./specs/cpu-all.json'); var devices = require('./specs/device-all.json'); diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-browser.spec.mjs new file mode 100644 index 000000000..a8efd2265 --- /dev/null +++ b/test/playwright-test-browser.spec.mjs @@ -0,0 +1,44 @@ +// @ts-check +import { test, expect } from '@playwright/test'; +import path from 'path'; +import url from 'url'; + +test('read client hints data', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgentData', { + value : { + brands : [], + platform : '', + mobile : false, + getHighEntropyValues : () => { + return Promise.resolve({ + brands : [ + { + brand : 'Chromium', + version : '110' + }, + { + brand : 'Not(A:Brand', + version : '110' + }, + { + brand : 'New Browser', + version : '110' + } + ], + platform : 'New OS' + }); + } + } + }); + }); + + const dirname = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../'); + await page.goto(`file://${dirname}/dist/ua-parser.html`); + + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser().withClientHints()); + + expect(uap).toHaveProperty('browser.name', 'New Browser'); + expect(uap).toHaveProperty('os.name', 'New OS'); +}); \ No newline at end of file From 16b416d9ead3853e0a3bd73e39ad779e22fd7d38 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 9 Apr 2023 05:34:02 +0700 Subject: [PATCH 071/388] Move feature detection into its own method: `withFeatureCheck` --- .gitignore | 1 + changelog.md | 1 + readme.md | 15 +++++ src/ua-parser.js | 22 ++++++- test/playwright-test-browser.spec.mjs | 89 +++++++++++++++++---------- 5 files changed, 91 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 0cd892f4d..4a99144dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ npm-debug.log playwright-report/ +test-results/ ### vim ### .*.s[a-w][a-z] diff --git a/changelog.md b/changelog.md index 76d4ef8ec..ac8e493be 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - What's new: - Add some new methods in result object: - Add support for client hints: `withClientHints()` + - Add support for feature detection: `withFeatureCheck()` - Utility for easy comparison: `is()` - Utility to print full-name: `toString()` - Add support for ES module `import { UAParser } from 'ua-parser-js'` diff --git a/readme.md b/readme.md index 999898593..b73df8d65 100644 --- a/readme.md +++ b/readme.md @@ -346,6 +346,21 @@ new UAParser(request.headers) }); ``` +#### * `withFeatureCheck():object` `since@2.0` + +This method allows us to examine other features beyond `navigator.userAgent` to further improve detection of the following: +- browser : Brave (check for `navigator.isBrave`) +- device : iPad (check for `navigator.standalone` & `navigator.maxTouchPoints`) + +```js +// suppose this code runs on iPad +const withoutFeatureCheck = UAParser(); +const withFeatureCheck = UAParser().withFeatureCheck(); + +console.log(withoutFeatureCheck.device); // { vendor : "Apple", model : "Macintosh", type : undefined } +console.log(withFeatureCheck.device); // { vendor : "Apple", model : "iPad", type : "tablet" } +``` + ## Extending Regex If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own. diff --git a/src/ua-parser.js b/src/ua-parser.js index 1e04b5940..68ac98d66 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -862,6 +862,10 @@ }); }; + UAParserData.prototype.withFeatureCheck = function () { + return item.detectFeature().get(); + }; + if (itemType != UA_RESULT) { UAParserData.prototype.is = function (strToCheck) { var is = false; @@ -978,6 +982,20 @@ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); } + break; + case UA_RESULT: + var data = this.data; + var detect = function (itemType) { + return data[itemType] + .getItem() + .detectFeature() + .get(); + }; + this.set(UA_BROWSER, detect(UA_BROWSER)) + .set(UA_CPU, detect(UA_CPU)) + .set(UA_DEVICE, detect(UA_DEVICE)) + .set(UA_ENGINE, detect(UA_ENGINE)) + .set(UA_OS, detect(UA_OS)); } } return this; @@ -1046,8 +1064,7 @@ .parseCH() .get(); }; - this.set('ua', ua) - .set(UA_BROWSER, parse(UA_BROWSER)) + this.set(UA_BROWSER, parse(UA_BROWSER)) .set(UA_CPU, parse(UA_CPU)) .set(UA_DEVICE, parse(UA_DEVICE)) .set(UA_ENGINE, parse(UA_ENGINE)) @@ -1118,7 +1135,6 @@ return function () { return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) .parseUA() - .detectFeature() .get(); }; } diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-browser.spec.mjs index a8efd2265..a3fb5b890 100644 --- a/test/playwright-test-browser.spec.mjs +++ b/test/playwright-test-browser.spec.mjs @@ -3,42 +3,63 @@ import { test, expect } from '@playwright/test'; import path from 'path'; import url from 'url'; +const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../')}/dist/ua-parser.html`; + test('read client hints data', async ({ page }) => { - await page.addInitScript(() => { - Object.defineProperty(navigator, 'userAgentData', { - value : { - brands : [], - platform : '', - mobile : false, - getHighEntropyValues : () => { - return Promise.resolve({ - brands : [ - { - brand : 'Chromium', - version : '110' - }, - { - brand : 'Not(A:Brand', - version : '110' - }, - { - brand : 'New Browser', - version : '110' - } - ], - platform : 'New OS' - }); - } - } + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgentData', { + value: { + brands: [], + platform: '', + mobile: false, + getHighEntropyValues: () => { + return Promise.resolve({ + brands: [ + { + brand: 'Chromium', + version: '110' + }, + { + brand: 'Not(A:Brand', + version: '110' + }, + { + brand: 'New Browser', + version: '110' + } + ], + platform: 'New OS' + }); + } + } + }); }); - }); - const dirname = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../'); - await page.goto(`file://${dirname}/dist/ua-parser.html`); + await page.goto(localHtml); + + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser().withClientHints()); + + expect(uap).toHaveProperty('browser.name', 'New Browser'); + expect(uap).toHaveProperty('os.name', 'New OS'); +}); + +test('detect Brave', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'brave', { + value: { + isBrave: () => true + } + }); + }); + + await page.goto(localHtml); + + // @ts-ignore + let uap = await page.evaluate(() => UAParser()); + expect(uap).toHaveProperty('browser.name', 'Chrome Headless'); - // @ts-ignore - const uap = await page.evaluate(async () => await UAParser().withClientHints()); - - expect(uap).toHaveProperty('browser.name', 'New Browser'); - expect(uap).toHaveProperty('os.name', 'New OS'); + // @ts-ignore + uap = await page.evaluate(() => UAParser().withFeatureCheck()); + expect(uap).toHaveProperty('browser.name', 'Brave'); }); \ No newline at end of file From 407b23262ce0f792e892fafdbfc8569702f52d2d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 9 Apr 2023 09:04:48 +0700 Subject: [PATCH 072/388] Update actions to v3 --- .github/workflows/run-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml index 203184294..5ce36be7d 100644 --- a/.github/workflows/run-test.yml +++ b/.github/workflows/run-test.yml @@ -12,8 +12,8 @@ jobs: matrix: arch: [amd64, ppc64le] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 - name: Run the test run: | npm install From 07c9e36ebedb0b6a0935150375d9c9af40daeee8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 9 Apr 2023 10:13:08 +0700 Subject: [PATCH 073/388] Update actions to install playwright & run build before test --- .github/workflows/run-test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml index 5ce36be7d..7a4cc58ce 100644 --- a/.github/workflows/run-test.yml +++ b/.github/workflows/run-test.yml @@ -14,7 +14,12 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + cache: 'npm' - name: Run the test run: | npm install + npm run build + npx playwright install npm run test-ci From a8951ec282ad27df8274c165d5f41ac0ad20f7c1 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 9 Apr 2023 10:21:20 +0700 Subject: [PATCH 074/388] Update actions to remove cache --- .github/workflows/run-test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml index 7a4cc58ce..4ed93e946 100644 --- a/.github/workflows/run-test.yml +++ b/.github/workflows/run-test.yml @@ -16,7 +16,6 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 'lts/*' - cache: 'npm' - name: Run the test run: | npm install From 801c2409b3e0fb83f67e95ebeca2723adb4976c9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 9 Apr 2023 15:54:46 +0700 Subject: [PATCH 075/388] Update readme & IData explanations --- dist/ua-parser.min.js | 2 +- dist/ua-parser.pack.js | 2 +- readme.md | 125 +++++++++++++++++++++++++-------------- src/module/ua-parser.mjs | 62 ++++++++++++------- src/ua-parser.js | 42 ++++++------- 5 files changed, 143 insertions(+), 90 deletions(-) diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index d5b153cca..e18ba06a7 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set("ua",ua).set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createUAParserData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UAParserDataCH(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAParserItem(itemType,userAgent,regexMap,HTTP_UACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAParserItem(itemType,userAgent,regexMap[itemType],HTTP_UACH).parseUA().detectFeature().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,HTTP_UACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],HTTP_UACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 141057c29..59ce392a0 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-alpha.2 Copyright © 2012-2023 Faisal Salman MIT License */ -!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i,this}]]).setUA(o),this}Ni.VERSION="2.0.0-alpha.2",Ni.BROWSER=e([h,m,l]),Ni.CPU=e([g]),Ni.DEVICE=e([p,o,f,r,v,x,a,k,y]),Ni.ENGINE=Ni.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ni),exports.UAParser=Ni):typeof define===c&&define.amd?define(function(){return Ni}):typeof i!==b&&(i.UAParser=Ni);var Oi,zi=typeof i!==b&&(i.jQuery||i.Zepto);zi&&!zi.ua&&(Oi=new Ni,zi.ua=Oi.getResult(),zi.ua.get=function(){return Oi.getUA()},zi.ua.set=function(i){Oi.setUA(i);var e,t=Oi.getResult();for(e in t)zi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i,this}]]).setUA(o),this}Ni.VERSION="2.0.0-alpha.2",Ni.BROWSER=e([h,m,l]),Ni.CPU=e([g]),Ni.DEVICE=e([p,o,f,r,v,x,a,k,y]),Ni.ENGINE=Ni.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ni),exports.UAParser=Ni):typeof define===c&&define.amd?define(function(){return Ni}):typeof i!==b&&(i.UAParser=Ni);var Oi,zi=typeof i!==b&&(i.jQuery||i.Zepto);zi&&!zi.ua&&(Oi=new Ni,zi.ua=Oi.getResult(),zi.ua.get=function(){return Oi.getUA()},zi.ua.set=function(i){Oi.setUA(i);var e,t=Oi.getResult();for(e in t)zi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/readme.md b/readme.md index b73df8d65..ed4c749d0 100644 --- a/readme.md +++ b/readme.md @@ -3,11 +3,13 @@

- - - - - + + + + + + +

# UAParser.js @@ -47,39 +49,47 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro What's new & breaking, please read [CHANGELOG](changelog.md) before upgrading. # Documentation -### UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]) - -In the browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in' nodejs for the function to work. Usually you can find the user agent in: `request.headers["user-agent"]`. +### `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData` +In browser environment you don't need to pass the user-agent string to the function, as it should automatically get the string from the `window.navigator.userAgent`. Whereas in nodejs environment, the user-agent string must be passed in order for the function to work (usually you can find the user-agent in: `request.headers["user-agent"]`). ## Constructor + +#### * `new UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):UAParser` + When you call `UAParser` with the `new` keyword, `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string. Like so: -* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])` + ```js let parser = new UAParser("your user-agent here"); // you need to pass the user-agent for nodejs console.log(parser); // {} let parserResults = parser.getResult(); console.log(parserResults); -/** { - "ua" : "", - "browser" : {}, - "engine" : {}, - "os" : {}, - "device" : {}, - "cpu" : {} -} */ +/* + { + ua : "", + browser : {}, + engine : {}, + os : {}, + device : {}, + cpu : {} + } +*/ ``` -When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results. -* `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])` - * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` +#### * `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData` + +When you call `UAParser` without the `new` keyword, it will automatically call `getResult()` function and return the parsed results. + +```sh +returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` +``` -## Methods +## `UAParser`: #### Methods table The methods are self explanatory, here's a small overview on all the available methods: -* `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os: + * `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os: `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`. * `getBrowser()` - returns the browser name and version. @@ -88,17 +98,21 @@ The methods are self explanatory, here's a small overview on all the available m * `getOS()` - returns the running operating system name and version. * `getCPU()` - returns CPU architectural design name. * `getUA()` - returns the user-agent string. - * `setUA(user-agent)` - set a custom user-agent to be parsed. + * `setUA(ua)` - set a custom user-agent to be parsed. --- -* `getResult()` - * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` +#### * `getResult():IData` + +```sh +returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` +``` -* `getBrowser()` - * returns `{ name: '', version: '' }` +#### * `getBrowser():IData` ```sh +returns `{ name: '', version: '' }` + # Possible 'browser.name': 2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG, BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera, @@ -119,10 +133,11 @@ Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ... # 'browser.version' determined dynamically ``` -* `getDevice()` - * returns `{ model: '', type: '', vendor: '' }` +#### * `getDevice():IData` ```sh +returns `{ model: '', type: '', vendor: '' }` + # Possible 'device.type': console, mobile, tablet, smarttv, wearable, embedded @@ -143,10 +158,11 @@ Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE # 'device.model' determined dynamically ``` -* `getEngine()` - * returns `{ name: '', version: '' }` +#### * `getEngine():IData` ```sh +returns `{ name: '', version: '' }` + # Possible 'engine.name' Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, LibWeb, Links, Lynx, NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit @@ -154,10 +170,11 @@ NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit # 'engine.version' determined dynamically ``` -* `getOS()` - * returns `{ name: '', version: '' }` +#### * `getOS():IData` ```sh +returns `{ name: '', version: '' }` + # Possible 'os.name' AIX, Amiga OS, Android[-x86], Arch, Bada, BeOS, BlackBerry, CentOS, Chromium OS, Contiki, Fedora, Firefox OS, FreeBSD, Debian, Deepin, DragonFly, elementary OS, @@ -172,22 +189,42 @@ Zenwalk, ... # 'os.version' determined dynamically ``` -* `getCPU()` - * returns `{ architecture: '' }` +#### * `getCPU():IData` ```sh +returns `{ architecture: '' }` + # Possible 'cpu.architecture' 68k, amd64, arm[64/hf], avr, ia[32/64], irix[64], mips[64], pa-risc, ppc, sparc[64] ``` -* `getUA()` - * returns UA string of current instance +#### * `getUA():string` + +```sh +returns user-agent string of current instance +``` + +#### * `setUA(ua:string):UAParser` + +```sh +set user-agent string to be parsed +returns current instance +``` -* `setUA(uastring)` - * set UA string to be parsed - * returns current instance +--- + +## `IData`: `since@2.0` + +#### Methods table +The methods are self explanatory, here's a small overview on all the available methods: + * `is(value)` - returns `true` if the passed value matches a value of current object, `false` otherwise + * `toString()` - returns the full-name values of current object as a string + * `withClientHints()` - returns an object with re-updated data from client hints + * `withFeatureCheck()` - returns an object with re-updated data from feature detection + +--- -#### * `is():boolean` utility `since@2.0` +#### * `is(value:string):boolean` ```js // Is just a shorthand comparison to check whether the value of specified item equals one of its properties (in a case-insensitive way) @@ -246,7 +283,7 @@ let engine = uap.getEngine(); engine.is("Blink"); // true ``` -#### * `toString():string` utility `since@2.0` +#### * `toString():string` ```js // Retrieve full-name values as a string @@ -287,7 +324,7 @@ engine.version; // "28.0.1500.95" engine.toString(); // "Blink 28.0.1500.95" ``` -#### * `withClientHints():Promise|Thenable|object` `since@2.0` +#### * `withClientHints():Promise|Thenable|IData` Recently, Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`. @@ -346,7 +383,7 @@ new UAParser(request.headers) }); ``` -#### * `withFeatureCheck():object` `since@2.0` +#### * `withFeatureCheck():IData` This method allows us to examine other features beyond `navigator.userAgent` to further improve detection of the following: - browser : Brave (check for `navigator.isBrave`) diff --git a/src/module/ua-parser.mjs b/src/module/ua-parser.mjs index ba554e354..250eea72a 100644 --- a/src/module/ua-parser.mjs +++ b/src/module/ua-parser.mjs @@ -829,22 +829,22 @@ return props; })(); - var createUAParserData = function (item, itemType) { + var createIData = function (item, itemType) { var init_props = defaultProps.init[itemType], is_ignoreProps = defaultProps.isIgnore[itemType] || 0, is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0, toString_props = defaultProps.toString[itemType] || 0; - function UAParserData () { + function IData () { setProps.call(this, init_props); } - UAParserData.prototype.getItem = function () { + IData.prototype.getItem = function () { return item; }; - UAParserData.prototype.withClientHints = function () { + IData.prototype.withClientHints = function () { // nodejs / non-client-hints browsers if (!NAVIGATOR_UADATA) { @@ -858,14 +858,18 @@ .getHighEntropyValues(CH_ALL_VALUES) .then(function (res) { return item - .setCH(new UAParserDataCH(res, false)) + .setCH(new UACHData(res, false)) .parseCH() .get(); }); }; + IData.prototype.withFeatureCheck = function () { + return item.detectFeature().get(); + }; + if (itemType != UA_RESULT) { - UAParserData.prototype.is = function (strToCheck) { + IData.prototype.is = function (strToCheck) { var is = false; for (var i in this) { if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) { @@ -878,7 +882,7 @@ } return is; }; - UAParserData.prototype.toString = function () { + IData.prototype.toString = function () { var str = EMPTY; for (var i in toString_props) { if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { @@ -890,33 +894,33 @@ } if (!NAVIGATOR_UADATA) { - UAParserData.prototype.then = function (cb) { + IData.prototype.then = function (cb) { var that = this; - var UAParserDataResolve = function () { + var IDataResolve = function () { for (var prop in that) { if (that.hasOwnProperty(prop)) { this[prop] = that[prop]; } } }; - UAParserDataResolve.prototype = { - is : UAParserData.prototype.is, - toString : UAParserData.prototype.toString + IDataResolve.prototype = { + is : IData.prototype.is, + toString : IData.prototype.toString }; - var resolveData = new UAParserDataResolve(); + var resolveData = new IDataResolve(); cb(resolveData); return resolveData; }; } - return new UAParserData(); + return new IData(); }; ///////////////// // Constructor //////////////// - function UAParserDataCH (uach, isHTTP_UACH) { + function UACHData (uach, isHTTP_UACH) { uach = uach || {}; setProps.call(this, CH_ALL_VALUES); if (isHTTP_UACH) { @@ -938,7 +942,7 @@ } } - function UAParserItem (itemType, ua, rgxMap, uaCH) { + function UAItem (itemType, ua, rgxMap, uaCH) { this.get = function (prop) { if (!prop) return this.data; @@ -980,6 +984,20 @@ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); } + break; + case UA_RESULT: + var data = this.data; + var detect = function (itemType) { + return data[itemType] + .getItem() + .detectFeature() + .get(); + }; + this.set(UA_BROWSER, detect(UA_BROWSER)) + .set(UA_CPU, detect(UA_CPU)) + .set(UA_DEVICE, detect(UA_DEVICE)) + .set(UA_ENGINE, detect(UA_ENGINE)) + .set(UA_OS, detect(UA_OS)); } } return this; @@ -1048,8 +1066,7 @@ .parseCH() .get(); }; - this.set('ua', ua) - .set(UA_BROWSER, parse(UA_BROWSER)) + this.set(UA_BROWSER, parse(UA_BROWSER)) .set(UA_CPU, parse(UA_CPU)) .set(UA_DEVICE, parse(UA_DEVICE)) .set(UA_ENGINE, parse(UA_ENGINE)) @@ -1063,7 +1080,7 @@ ['ua', ua], ['uaCH', uaCH], ['rgxMap', rgxMap], - ['data', createUAParserData(this, itemType)] + ['data', createIData(this, itemType)] ]); return this; @@ -1098,7 +1115,7 @@ headers[USER_AGENT] : EMPTY)), - HTTP_UACH = new UAParserDataCH(headers, true), + HTTP_UACH = new UACHData(headers, true), regexMap = extensions ? extend(defaultRegexes, extensions) : @@ -1107,7 +1124,7 @@ createItemFunc = function (itemType) { if (itemType == UA_RESULT) { return function () { - return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH) + return new UAItem(itemType, userAgent, regexMap, HTTP_UACH) .set('ua', userAgent) .set(UA_BROWSER, this.getBrowser()) .set(UA_CPU, this.getCPU()) @@ -1118,9 +1135,8 @@ }; } else { return function () { - return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) + return new UAItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) .parseUA() - .detectFeature() .get(); }; } diff --git a/src/ua-parser.js b/src/ua-parser.js index 68ac98d66..d72fda7bb 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -827,22 +827,22 @@ return props; })(); - var createUAParserData = function (item, itemType) { + var createIData = function (item, itemType) { var init_props = defaultProps.init[itemType], is_ignoreProps = defaultProps.isIgnore[itemType] || 0, is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0, toString_props = defaultProps.toString[itemType] || 0; - function UAParserData () { + function IData () { setProps.call(this, init_props); } - UAParserData.prototype.getItem = function () { + IData.prototype.getItem = function () { return item; }; - UAParserData.prototype.withClientHints = function () { + IData.prototype.withClientHints = function () { // nodejs / non-client-hints browsers if (!NAVIGATOR_UADATA) { @@ -856,18 +856,18 @@ .getHighEntropyValues(CH_ALL_VALUES) .then(function (res) { return item - .setCH(new UAParserDataCH(res, false)) + .setCH(new UACHData(res, false)) .parseCH() .get(); }); }; - UAParserData.prototype.withFeatureCheck = function () { + IData.prototype.withFeatureCheck = function () { return item.detectFeature().get(); }; if (itemType != UA_RESULT) { - UAParserData.prototype.is = function (strToCheck) { + IData.prototype.is = function (strToCheck) { var is = false; for (var i in this) { if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) { @@ -880,7 +880,7 @@ } return is; }; - UAParserData.prototype.toString = function () { + IData.prototype.toString = function () { var str = EMPTY; for (var i in toString_props) { if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { @@ -892,33 +892,33 @@ } if (!NAVIGATOR_UADATA) { - UAParserData.prototype.then = function (cb) { + IData.prototype.then = function (cb) { var that = this; - var UAParserDataResolve = function () { + var IDataResolve = function () { for (var prop in that) { if (that.hasOwnProperty(prop)) { this[prop] = that[prop]; } } }; - UAParserDataResolve.prototype = { - is : UAParserData.prototype.is, - toString : UAParserData.prototype.toString + IDataResolve.prototype = { + is : IData.prototype.is, + toString : IData.prototype.toString }; - var resolveData = new UAParserDataResolve(); + var resolveData = new IDataResolve(); cb(resolveData); return resolveData; }; } - return new UAParserData(); + return new IData(); }; ///////////////// // Constructor //////////////// - function UAParserDataCH (uach, isHTTP_UACH) { + function UACHData (uach, isHTTP_UACH) { uach = uach || {}; setProps.call(this, CH_ALL_VALUES); if (isHTTP_UACH) { @@ -940,7 +940,7 @@ } } - function UAParserItem (itemType, ua, rgxMap, uaCH) { + function UAItem (itemType, ua, rgxMap, uaCH) { this.get = function (prop) { if (!prop) return this.data; @@ -1078,7 +1078,7 @@ ['ua', ua], ['uaCH', uaCH], ['rgxMap', rgxMap], - ['data', createUAParserData(this, itemType)] + ['data', createIData(this, itemType)] ]); return this; @@ -1113,7 +1113,7 @@ headers[USER_AGENT] : EMPTY)), - HTTP_UACH = new UAParserDataCH(headers, true), + HTTP_UACH = new UACHData(headers, true), regexMap = extensions ? extend(defaultRegexes, extensions) : @@ -1122,7 +1122,7 @@ createItemFunc = function (itemType) { if (itemType == UA_RESULT) { return function () { - return new UAParserItem(itemType, userAgent, regexMap, HTTP_UACH) + return new UAItem(itemType, userAgent, regexMap, HTTP_UACH) .set('ua', userAgent) .set(UA_BROWSER, this.getBrowser()) .set(UA_CPU, this.getCPU()) @@ -1133,7 +1133,7 @@ }; } else { return function () { - return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) + return new UAItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) .parseUA() .get(); }; From 2fb0c728981efd44a14001a2de901182b80066af Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Apr 2023 10:59:46 +0700 Subject: [PATCH 076/388] Create deploy-docs.yml --- .github/workflows/deploy-docs.yml | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/deploy-docs.yml diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 000000000..b6c547aa7 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,33 @@ +name: Deploy VitePress +on: + workflow_dispatch: {} + push: + branches: + - gh-pages +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-node@v3 + with: + node-version: 16 + cache: npm + - run: npm ci + - name: Build + run: npm run docs:build + - uses: actions/configure-pages@v2 + - uses: actions/upload-pages-artifact@v1 + with: + path: docs/v2/.vitepress/dist + - name: Deploy + id: deployment + uses: actions/deploy-pages@v1 From e62cded08303c790a537ab8ba08bcd65258d15d4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Apr 2023 11:24:11 +0700 Subject: [PATCH 077/388] Update deploy-docs.yml --- .github/workflows/deploy-docs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index b6c547aa7..cb081e648 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -20,7 +20,6 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 16 - cache: npm - run: npm ci - name: Build run: npm run docs:build From ff268137086551cd0e95a4f2c99830f3bf3425dd Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Apr 2023 11:26:11 +0700 Subject: [PATCH 078/388] Update deploy-docs.yml - again --- .github/workflows/deploy-docs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index cb081e648..6e716046e 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -20,7 +20,6 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 16 - - run: npm ci - name: Build run: npm run docs:build - uses: actions/configure-pages@v2 From 46f38adb838796636f9429a58a72bd64aa2e8bc8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Apr 2023 23:06:26 +0700 Subject: [PATCH 079/388] Remove deploy-docs.yml - Move docs to another repo --- .github/workflows/deploy-docs.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/deploy-docs.yml diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml deleted file mode 100644 index 6e716046e..000000000 --- a/.github/workflows/deploy-docs.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Deploy VitePress -on: - workflow_dispatch: {} - push: - branches: - - gh-pages -jobs: - deploy: - runs-on: ubuntu-latest - permissions: - pages: write - id-token: write - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - uses: actions/setup-node@v3 - with: - node-version: 16 - - name: Build - run: npm run docs:build - - uses: actions/configure-pages@v2 - - uses: actions/upload-pages-artifact@v1 - with: - path: docs/v2/.vitepress/dist - - name: Deploy - id: deployment - uses: actions/deploy-pages@v1 From 99baf60d50ae3a0e944689e4aa2f2e00211f6bcf Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 12 Apr 2023 11:48:00 +0700 Subject: [PATCH 080/388] Fix #608 - Add OpenSSF Scorecard GitHub Action --- .github/workflows/scorecard.yml | 72 +++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/workflows/scorecard.yml diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..5844bc19a --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,72 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '45 2 * * 4' + push: + branches: [ "master" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 + with: + sarif_file: results.sarif From f5af76a2b3a447c882fd6ddedf65b2f4f5c0f4d7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 12 Apr 2023 13:16:41 +0700 Subject: [PATCH 081/388] Create GitHub packaging workflow --- .../workflows/npm-publish-github-packages.yml | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/npm-publish-github-packages.yml diff --git a/.github/workflows/npm-publish-github-packages.yml b/.github/workflows/npm-publish-github-packages.yml new file mode 100644 index 000000000..47a37f98c --- /dev/null +++ b/.github/workflows/npm-publish-github-packages.yml @@ -0,0 +1,36 @@ +# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created +# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages + +name: Node.js Package + +on: + release: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + - run: npm ci + - run: npm test + + publish-gpr: + needs: build + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + registry-url: https://npm.pkg.github.com/ + - run: npm ci + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} From c2f17004b862e9b692eeaa155cdd072b0c149072 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 13 Apr 2023 06:08:52 +0700 Subject: [PATCH 082/388] Add GitHub's CodeQL Action for static code analysis --- .github/workflows/codeql.yml | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..1db5e9812 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,76 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master"] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '15 6 * * 0' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + # - name: Autobuild + # uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From 432a2ee72d2354fea6c9d088bff54c453bcddf2b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 13 Apr 2023 05:04:16 +0700 Subject: [PATCH 083/388] Pin dependency hash with lockfile & test with lockfile-lint --- .github/workflows/run-test.yml | 2 +- .npmrc | 1 - package-lock.json | 1813 ++++++++++++++++++++++++++++++++ package.json | 8 +- 4 files changed, 1818 insertions(+), 6 deletions(-) delete mode 100644 .npmrc create mode 100644 package-lock.json diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml index 4ed93e946..b3990c8f5 100644 --- a/.github/workflows/run-test.yml +++ b/.github/workflows/run-test.yml @@ -18,7 +18,7 @@ jobs: node-version: 'lts/*' - name: Run the test run: | - npm install + npm ci npm run build npx playwright install npm run test-ci diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 43c97e719..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..d696d4bcb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1813 @@ +{ + "name": "ua-parser-js", + "version": "2.0.0-alpha.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ua-parser-js", + "version": "2.0.0-alpha.2", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "devDependencies": { + "@babel/parser": "7.15.8", + "@babel/traverse": "7.15.4", + "@playwright/test": "~1.32.2", + "jshint": "~2.12.0", + "mocha": "~8.2.0", + "requirejs": "2.3.2", + "safe-regex": "^2.1.1", + "uglify-js": "~3.12.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", + "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.4", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/parser": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", + "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@playwright/test": { + "version": "1.32.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz", + "integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "playwright-core": "1.32.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/@types/node": { + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "dev": true + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.2" + } + }, + "node_modules/cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==", + "dev": true, + "dependencies": { + "exit": "0.1.2", + "glob": "^7.1.1" + }, + "engines": { + "node": ">=0.2.5" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha512-duS7VP5pvfsNLDvL1O4VOEbw37AI3A4ZUQYemvDlnpGrNu9tprR7BYWpDYwC0Xia0Zxz5ZupdiIrUp0GH1aXfg==", + "dev": true, + "dependencies": { + "date-now": "^0.1.4" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha512-q9bUwjfp7Eif8jWxxxPSykdRZAb6GkguBGSgvvCrhI9wB71W2K/Kvv4E61CF/mcCfnVJDeDWx/Vb/uAqbDj6UQ==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha512-hBxEg3CYXe+rPIua8ETe7tmG3XDn9B0edOE/e9wH2nLczxzgdu0m0aNHY+5wFZiviLWLdANPJTssa92dMcXQ5Q==", + "dev": true, + "dependencies": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jshint": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz", + "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==", + "dev": true, + "dependencies": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.19", + "minimatch": "~3.0.2", + "shelljs": "0.3.x", + "strip-json-comments": "1.0.x" + }, + "bin": { + "jshint": "bin/jshint" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.3", + "debug": "4.2.0", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.2", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || >=13.7" + } + }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright-core": { + "version": "1.32.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.3.tgz", + "integrity": "sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==", + "dev": true, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", + "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/requirejs": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.2.tgz", + "integrity": "sha512-rGSBJSIWfUEkMTztpZlyGIpRIwuMau6gZYxQhxH1jCggR3ddWsI8+/aWh9yUscMB1O0pEgZrJiXy2hJerzMszA==", + "dev": true, + "bin": { + "r_js": "bin/r.js", + "r.js": "bin/r.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "dependencies": { + "regexp-tree": "~0.1.1" + } + }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha512-Ny0KN4dyT8ZSCE0frtcbAJGoM/HTArpyPkeli1/00aYfm0sbD/Gk/4x7N2DP9QKGpBsiQH7n6rpm1L79RtviEQ==", + "dev": true, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg==", + "dev": true, + "bin": { + "strip-json-comments": "cli.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/uglify-js": { + "version": "3.12.8", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", + "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==", + "dev": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/workerpool": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index faba6cc57..451baa138 100644 --- a/package.json +++ b/package.json @@ -164,16 +164,16 @@ ], "scripts": { "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node script/build-module.js", - "test": "jshint src && mocha -R nyan test/mocha*js && npx playwright test", - "test-ci": "jshint src && mocha -R spec test/mocha*js && npx playwright test" + "test": "jshint src && mocha -R nyan test/mocha*js && npx playwright test && npx lockfile-lint -p package-lock.json", + "test-ci": "jshint src && mocha -R spec test/mocha*js && npx playwright test && npx lockfile-lint -p package-lock.json" }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", - "@playwright/test": "^1.32.2", + "@playwright/test": "~1.32.2", "jshint": "~2.12.0", "mocha": "~8.2.0", - "requirejs": "^2.3.2", + "requirejs": "2.3.2", "safe-regex": "^2.1.1", "uglify-js": "~3.12.0" }, From 6c58ac39cd6b2d5497b4598f81254acce02023d8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 13 Apr 2023 05:58:06 +0700 Subject: [PATCH 084/388] Update security policy --- security.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security.md b/security.md index de525bbf5..d1f40fe80 100644 --- a/security.md +++ b/security.md @@ -2,4 +2,6 @@ ## Reporting a Vulnerability -Please report security issues to `f@faisalman.com` +To report a security issue, please email `f@faisalman.com` with a description of the issue, reproducible steps to get the issue, affected versions, and, if known, mitigations for the issue. + +If the issue is confirmed as a vulnerability, we will open a new security advisory draft in our GitHub's Security Advisory page [https://github.com/faisalman/ua-parser-js/security/advisories](https://github.com/faisalman/ua-parser-js/security/advisories) and acknowledge your contributions as part of it. This project follows a 90 days disclosure timeline. \ No newline at end of file From f65965950047e928b549a4d39ae545e605d3ced1 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 13 Apr 2023 06:24:11 +0700 Subject: [PATCH 085/388] Set CodeQL permission to read-only --- .github/workflows/codeql.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1db5e9812..0f42cb1ff 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,6 +20,8 @@ on: schedule: - cron: '15 6 * * 0' +permissions: read-all + jobs: analyze: name: Analyze From f92bb9ef65eb34f3a8c74241477624e588ffa42f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 13 Apr 2023 06:37:59 +0700 Subject: [PATCH 086/388] Insert scorecard badge & documentation link --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index ed4c749d0..7b669d966 100644 --- a/readme.md +++ b/readme.md @@ -10,6 +10,7 @@ +

# UAParser.js @@ -19,6 +20,7 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro * Author : Faisal Salman <> * Demo : https://faisalman.github.io/ua-parser-js * Source : https://github.com/faisalman/ua-parser-js +* Documentation : https://faisalman.github.io/ua-parser-js-docs/v2 *** From 1b173159350b23c134cbf3735d9a6c93d686cae5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 13 Apr 2023 06:56:27 +0700 Subject: [PATCH 087/388] Create action to review dependencies from PR --- .github/workflows/dependency-review.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/dependency-review.yml diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000..fe461b424 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,20 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v3 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v2 From 0ac502813779e45eb1181b1bec874dce2043eeaa Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 13 Apr 2023 07:07:34 +0700 Subject: [PATCH 088/388] Rename workflows for clarity --- .github/workflows/{codeql.yml => analysis-codeql.yml} | 2 +- .../{dependency-review.yml => analysis-dependency.yml} | 2 +- .github/workflows/{scorecard.yml => analysis-scorecard.yml} | 2 +- ...-publish-github-packages.yml => publish-github-packages.yml} | 2 +- .github/workflows/{run-test.yml => test-ci.yml} | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename .github/workflows/{codeql.yml => analysis-codeql.yml} (99%) rename .github/workflows/{dependency-review.yml => analysis-dependency.yml} (97%) rename .github/workflows/{scorecard.yml => analysis-scorecard.yml} (98%) rename .github/workflows/{npm-publish-github-packages.yml => publish-github-packages.yml} (96%) rename .github/workflows/{run-test.yml => test-ci.yml} (93%) diff --git a/.github/workflows/codeql.yml b/.github/workflows/analysis-codeql.yml similarity index 99% rename from .github/workflows/codeql.yml rename to .github/workflows/analysis-codeql.yml index 0f42cb1ff..b718e6878 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/analysis-codeql.yml @@ -9,7 +9,7 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: CodeQL Analysis on: push: diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/analysis-dependency.yml similarity index 97% rename from .github/workflows/dependency-review.yml rename to .github/workflows/analysis-dependency.yml index fe461b424..99afdf01c 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/analysis-dependency.yml @@ -4,7 +4,7 @@ # # Source repository: https://github.com/actions/dependency-review-action # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement -name: 'Dependency Review' +name: Dependency Analysis on: [pull_request] permissions: diff --git a/.github/workflows/scorecard.yml b/.github/workflows/analysis-scorecard.yml similarity index 98% rename from .github/workflows/scorecard.yml rename to .github/workflows/analysis-scorecard.yml index 5844bc19a..0c8f2b902 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/analysis-scorecard.yml @@ -2,7 +2,7 @@ # by a third-party and are governed by separate terms of service, privacy # policy, and support documentation. -name: Scorecard supply-chain security +name: OpenSSF's Scorecard Analysis on: # For Branch-Protection check. Only the default branch is supported. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection diff --git a/.github/workflows/npm-publish-github-packages.yml b/.github/workflows/publish-github-packages.yml similarity index 96% rename from .github/workflows/npm-publish-github-packages.yml rename to .github/workflows/publish-github-packages.yml index 47a37f98c..195c54547 100644 --- a/.github/workflows/npm-publish-github-packages.yml +++ b/.github/workflows/publish-github-packages.yml @@ -1,7 +1,7 @@ # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages -name: Node.js Package +name: Publish GitHub Package on: release: diff --git a/.github/workflows/run-test.yml b/.github/workflows/test-ci.yml similarity index 93% rename from .github/workflows/run-test.yml rename to .github/workflows/test-ci.yml index b3990c8f5..899ad44a8 100644 --- a/.github/workflows/run-test.yml +++ b/.github/workflows/test-ci.yml @@ -1,4 +1,4 @@ -name: ua-parser-js-run-test +name: UAParser.js CI-Test on: [push, pull_request] From feefb81cd0241f9a85ca72319c9eea3edd86dbf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Apr 2023 00:20:02 +0000 Subject: [PATCH 089/388] Bump shelljs and jshint Removes [shelljs](https://github.com/shelljs/shelljs). It's no longer used after updating ancestor dependency [jshint](https://github.com/jshint/jshint). These dependencies need to be updated together. Removes `shelljs` Updates `jshint` from 2.12.0 to 2.13.6 - [Release notes](https://github.com/jshint/jshint/releases) - [Changelog](https://github.com/jshint/jshint/blob/main/CHANGELOG.md) - [Commits](https://github.com/jshint/jshint/compare/2.12.0...2.13.6) --- updated-dependencies: - dependency-name: shelljs dependency-type: indirect - dependency-name: jshint dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 23 +++++------------------ package.json | 2 +- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index d696d4bcb..2e5b36611 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", "@playwright/test": "~1.32.2", - "jshint": "~2.12.0", + "jshint": "~2.13.6", "mocha": "~8.2.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", @@ -928,18 +928,17 @@ } }, "node_modules/jshint": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz", - "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==", + "version": "2.13.6", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.6.tgz", + "integrity": "sha512-IVdB4G0NTTeQZrBoM8C5JFVLjV2KtZ9APgybDA1MK73xb09qFs0jCXyQLnCOp1cSZZZbvhq/6mfXHUTaDkffuQ==", "dev": true, "dependencies": { "cli": "~1.0.0", "console-browserify": "1.1.x", "exit": "0.1.x", "htmlparser2": "3.8.x", - "lodash": "~4.17.19", + "lodash": "~4.17.21", "minimatch": "~3.0.2", - "shelljs": "0.3.x", "strip-json-comments": "1.0.x" }, "bin": { @@ -1431,18 +1430,6 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "node_modules/shelljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha512-Ny0KN4dyT8ZSCE0frtcbAJGoM/HTArpyPkeli1/00aYfm0sbD/Gk/4x7N2DP9QKGpBsiQH7n6rpm1L79RtviEQ==", - "dev": true, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", diff --git a/package.json b/package.json index 451baa138..32c627af8 100644 --- a/package.json +++ b/package.json @@ -171,7 +171,7 @@ "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", "@playwright/test": "~1.32.2", - "jshint": "~2.12.0", + "jshint": "~2.13.6", "mocha": "~8.2.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", From 35c2b91534e47fd17b254f1bdfa016d22f21a820 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 14 Apr 2023 15:36:37 +0700 Subject: [PATCH 090/388] Fix: accept empty string as input --- src/ua-parser.js | 23 ++++++++++------------- test/playwright-test-browser.spec.mjs | 12 ++++++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/ua-parser.js b/src/ua-parser.js index d72fda7bb..5f306252a 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -918,10 +918,10 @@ // Constructor //////////////// - function UACHData (uach, isHTTP_UACH) { + function UACHData (uach, isHttpUACH) { uach = uach || {}; setProps.call(this, CH_ALL_VALUES); - if (isHTTP_UACH) { + if (isHttpUACH) { setProps.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], @@ -1106,15 +1106,12 @@ return new UAParser(ua, extensions, headers).getResult(); } - var userAgent = ua || - ((NAVIGATOR && NAVIGATOR.userAgent) ? - NAVIGATOR.userAgent : - (headers && headers[USER_AGENT] ? - headers[USER_AGENT] : - EMPTY)), - - HTTP_UACH = new UACHData(headers, true), + var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string + ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent + (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers + EMPTY)), // empty string + httpUACH = new UACHData(headers, true), regexMap = extensions ? extend(defaultRegexes, extensions) : defaultRegexes, @@ -1122,7 +1119,7 @@ createItemFunc = function (itemType) { if (itemType == UA_RESULT) { return function () { - return new UAItem(itemType, userAgent, regexMap, HTTP_UACH) + return new UAItem(itemType, userAgent, regexMap, httpUACH) .set('ua', userAgent) .set(UA_BROWSER, this.getBrowser()) .set(UA_CPU, this.getCPU()) @@ -1133,13 +1130,13 @@ }; } else { return function () { - return new UAItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) + return new UAItem(itemType, userAgent, regexMap[itemType], httpUACH) .parseUA() .get(); }; } }; - + // public methods setProps.call(this, [ ['getBrowser', createItemFunc(UA_BROWSER)], diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-browser.spec.mjs index a3fb5b890..bab1f3a0b 100644 --- a/test/playwright-test-browser.spec.mjs +++ b/test/playwright-test-browser.spec.mjs @@ -5,6 +5,18 @@ import url from 'url'; const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../')}/dist/ua-parser.html`; +test.describe('test input', () => { + test.beforeEach(async ({ page }) => { + await page.goto(localHtml); + }); + + test('accept empty string', async ({ page }) => { + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser('')); + expect(uap).toHaveProperty('ua', ''); + }); +}); + test('read client hints data', async ({ page }) => { await page.addInitScript(() => { Object.defineProperty(navigator, 'userAgentData', { From 5a26ac146ee0c7afc3dd42d0cd93dbd14ceee4d6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 14 Apr 2023 23:43:09 +0700 Subject: [PATCH 091/388] Create build+test scripts --- .github/workflows/test-ci.yml | 3 +- package.json | 6 +-- script/build-dist.sh | 13 ++++++ script/build-module.js | 75 ++++++++++++++++++++--------------- script/test-all.sh | 21 ++++++++++ 5 files changed, 82 insertions(+), 36 deletions(-) create mode 100755 script/build-dist.sh mode change 100644 => 100755 script/build-module.js create mode 100755 script/test-all.sh diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml index 899ad44a8..5c02b3dc5 100644 --- a/.github/workflows/test-ci.yml +++ b/.github/workflows/test-ci.yml @@ -19,6 +19,5 @@ jobs: - name: Run the test run: | npm ci - npm run build npx playwright install - npm run test-ci + npm test diff --git a/package.json b/package.json index 32c627af8..81e29058d 100644 --- a/package.json +++ b/package.json @@ -163,9 +163,8 @@ "src" ], "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle && node script/build-module.js", - "test": "jshint src && mocha -R nyan test/mocha*js && npx playwright test && npx lockfile-lint -p package-lock.json", - "test-ci": "jshint src && mocha -R spec test/mocha*js && npx playwright test && npx lockfile-lint -p package-lock.json" + "build": "./script/build-dist.sh && ./script/build-module.js", + "test": "npm run build && ./script/test-all.sh" }, "devDependencies": { "@babel/parser": "7.15.8", @@ -187,6 +186,7 @@ }, "directories": { "dist": "dist", + "script": "script", "src": "src", "test": "test" }, diff --git a/script/build-dist.sh b/script/build-dist.sh new file mode 100755 index 000000000..4df2da7e8 --- /dev/null +++ b/script/build-dist.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +SRC_PATH="src/ua-parser.js" +MIN_PATH="dist/ua-parser.min.js" +PACK_PATH="dist/ua-parser.pack.js" + +# minified +echo "Generate ${MIN_PATH}" +uglifyjs $SRC_PATH -o $MIN_PATH --comments "/^ UA/" + +# packed +echo "Generate ${PACK_PATH}" +uglifyjs $SRC_PATH -o $PACK_PATH --comments "/^ UA/" --compress --mangle \ No newline at end of file diff --git a/script/build-module.js b/script/build-module.js old mode 100644 new mode 100755 index 5b47349dc..4d36273e7 --- a/script/build-module.js +++ b/script/build-module.js @@ -1,40 +1,53 @@ #!/usr/bin/env node +/* jshint esversion: 6 */ const fs = require('fs'); - -/*/////////////// -// ua-parser.mjs -//////////////*/ - -fs.writeFileSync('src/module/ua-parser.mjs', -`// Generated ESM version of UAParser.js +const PATH = { + main : { + src : 'src/ua-parser.js', + dest : 'src/module/ua-parser.mjs', + title : '' + }, + enum : { + src : 'src/ua-parser-enum.js', + dest :'src/module/ua-parser-enum.mjs', + title : 'enum' + }, + extension : { + src : 'src/ua-parser-extension.js', + dest : 'src/module/ua-parser-extension.mjs', + title : 'extension' + } +}; +const generateMJS = (module, replacers) => { + const { src, dest, title } = PATH[module]; + let text = fs.readFileSync(src, 'utf-8'); + replacers.forEach(replacer => { + text = text.replace(replacer[0], replacer[1]); + }); + + console.log(`Generate ${dest}`); + + fs.writeFileSync(dest, +`// Generated ESM version of UAParser.js ${title} // DO NOT EDIT THIS FILE! // Source: /src/ua-parser.js -` + fs.readFileSync('src/ua-parser.js','utf-8').replace(/\(func[\s\S]+strict\';/ig,'') - .replace(/esversion\: 3/ig, 'esversion: 6') - .replace(/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'),'utf-8'); - -/*///////////////////// -// ua-parser-enum.mjs -////////////////////*/ - -fs.writeFileSync('src/module/ua-parser-enum.mjs', -`// Generated ESM version of UAParser.js enums -// DO NOT EDIT THIS FILE! -// Source: /src/ua-parser-enum.js +${text}`, 'utf-8'); +}; -` + fs.readFileSync('src/ua-parser-enum.js','utf-8') - .replace(/module\.exports =/ig,'export'),'utf-8'); +if (!fs.existsSync('src/module')) { + fs.mkdirSync('src/module', { recursive: true }); +} -/*////////////////////////// -// ua-parser-extension.mjs -/////////////////////////*/ +// ua-parser.mjs +generateMJS('main', [ + [/\(func[\s\S]+strict\';/ig, ''], + [/esversion\: 3/ig, 'esversion: 6'], + [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'] +]); -fs.writeFileSync('src/module/ua-parser-extension.mjs', -`// Generated ESM version of UAParser.js extensions -// DO NOT EDIT THIS FILE! -// Source: /src/ua-parser-extension.js +// ua-parser-enum.mjs +generateMJS('enum', [[/module\.exports =/ig, 'export']]); -` + fs.readFileSync('src/ua-parser-extension.js','utf-8') - .replace(/const UA.+\)/ig,'import UAParser from \'ua-parser-js\'') - .replace(/module\.exports =/ig,'export'),'utf-8'); \ No newline at end of file +// ua-parser-extension.mjs +generateMJS('extension', [[/module\.exports =/ig, 'export']]); \ No newline at end of file diff --git a/script/test-all.sh b/script/test-all.sh new file mode 100755 index 000000000..b77df1a85 --- /dev/null +++ b/script/test-all.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +echo ' +- lint js code +' +jshint src && jshint script || exit 1 + +echo ' +- test using mocha +' +mocha -R list test/mocha*js || exit 1 + +echo ' +- test using playwright +' +npx playwright test || exit 1 + +echo ' +- lint lockfile +' +npx lockfile-lint -p package-lock.json || exit 1 \ No newline at end of file From c3be7326b86dec9f705fe0264a0b652e2c3b1ef6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 15 Apr 2023 00:01:15 +0700 Subject: [PATCH 092/388] Update GitHub package action - change trigger to 'published' --- .github/workflows/publish-github-packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml index 195c54547..eaea41c37 100644 --- a/.github/workflows/publish-github-packages.yml +++ b/.github/workflows/publish-github-packages.yml @@ -5,7 +5,7 @@ name: Publish GitHub Package on: release: - types: [created] + types: [published] jobs: build: From 29fb85658adb66e4b842ffe721b38e77f4ec5cd1 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 15 Apr 2023 01:08:46 +0700 Subject: [PATCH 093/388] Fix #643 - Improve iOS detection + detect Slack (&Slackbot) --- src/ua-parser-extension.js | 20 +++++++++++++++----- src/ua-parser.js | 2 +- test/mocha-test-extension.js | 12 ++++++------ test/specs/os-all.json | 9 +++++++++ 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/ua-parser-extension.js b/src/ua-parser-extension.js index fed987eb3..329129ebd 100644 --- a/src/ua-parser-extension.js +++ b/src/ua-parser-extension.js @@ -15,10 +15,19 @@ const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; +const Apps = Object.freeze({ + browser : [ + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']] + ] +}); + const Bots = Object.freeze({ browser : [ - // Googlebot / BingBot / MSNBot / FacebookBot - [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] + // Googlebot / BingBot / MSNBot / FacebookBot + [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + + // Slackbot - https://api.slack.com/robots + [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] ] }); @@ -107,16 +116,17 @@ const Emails = Object.freeze({ ] }); -const Tools = Object.freeze({ +const CLI = Object.freeze({ browser : [ // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']] + [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] ] }); module.exports = { + Apps, Bots, ExtraDevices, Emails, - Tools + CLI }; \ No newline at end of file diff --git a/src/ua-parser.js b/src/ua-parser.js index 5f306252a..13d5507c8 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -732,7 +732,7 @@ // iOS/macOS /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS - /ios;fbsv\/([\d\.]+)/i, + /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i, /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 250ee6442..c97c16d26 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -1,7 +1,7 @@ const assert = require('assert'); const safeRegex = require('safe-regex'); const UAParser = require('ua-parser-js'); -const { Bots, Emails, Tools } = require('ua-parser-js/extensions'); +const { Bots, Emails, CLI } = require('ua-parser-js/extensions'); describe('Bots', () => { it('Can detect bots', () => { @@ -20,11 +20,11 @@ describe('Bots', () => { assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"}); - // try merging Bots & Tools - const botsAndTools = { browser : [...Bots.browser, ...Tools.browser]}; - const botolParser = new UAParser(botsAndTools); - assert.deepEqual(botolParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"tool"}); - assert.deepEqual(botolParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); + // try merging Bots & CLIs + const botsAndCLIs = { browser : [...Bots.browser, ...CLI.browser]}; + const botsAndCLIsParser = new UAParser(botsAndCLIs); + assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); + assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); const emailParser = new UAParser(Emails); assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); diff --git a/test/specs/os-all.json b/test/specs/os-all.json index 3700e5339..5bf7630b0 100644 --- a/test/specs/os-all.json +++ b/test/specs/os-all.json @@ -773,6 +773,15 @@ "version" : "13.6.1" } }, + { + "desc": "iOS with Slack App", + "ua": "com.tinyspeck.chatlyio/23.04.10 (iPhone; iOS 16.4.1; Scale/3.00)", + "expect": + { + "name" : "iOS", + "version" : "16.4.1" + } + }, { "desc" : "watchOS", "ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]", From 1653d376ca61c7082ba6d0f22105f97ad931dfe6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 15 Apr 2023 13:45:31 +0700 Subject: [PATCH 094/388] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/custom.md | 10 ++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..dd84ea782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 000000000..48d5f81fa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 9102871dea6d877fc8f40b150e074694a56b13f4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 21 Apr 2023 09:47:51 +0700 Subject: [PATCH 095/388] Rearrange the structure of src folders --- package.json | 17 ++++++----- script/build-dist.sh | 2 +- script/build-module.js | 30 ++++++++----------- .../ua-parser-enums.js} | 0 .../ua-parser-enums.mjs} | 2 +- .../ua-parser-extensions.js} | 0 .../ua-parser-extensions.mjs} | 22 ++++++++++---- src/{ => main}/ua-parser.js | 0 src/{module => main}/ua-parser.mjs | 29 ++++++++---------- test/mocha-test.js | 4 +-- 10 files changed, 55 insertions(+), 51 deletions(-) rename src/{ua-parser-enum.js => enums/ua-parser-enums.js} (100%) rename src/{module/ua-parser-enum.mjs => enums/ua-parser-enums.mjs} (98%) rename src/{ua-parser-extension.js => extensions/ua-parser-extensions.js} (100%) rename src/{module/ua-parser-extension.mjs => extensions/ua-parser-extensions.mjs} (91%) rename src/{ => main}/ua-parser.js (100%) rename src/{module => main}/ua-parser.mjs (98%) diff --git a/package.json b/package.json index 81e29058d..dbe33f8d9 100644 --- a/package.json +++ b/package.json @@ -142,20 +142,21 @@ "Zach Bjornson " ], "type": "commonjs", - "main": "src/ua-parser.js", - "module": "src/module/ua-parser.mjs", + "main": "src/main/ua-parser.js", + "module": "src/main/ua-parser.mjs", + "browser": "dist/ua-parser.pack.js", "exports": { ".": { - "require": "./src/ua-parser.js", - "import": "./src/module/ua-parser.mjs" + "require": "./src/main/ua-parser.js", + "import": "./src/main/ua-parser.mjs" }, "./enums": { - "require": "./src/ua-parser-enum.js", - "import": "./src/module/ua-parser-enum.mjs" + "require": "./src/enums/ua-parser-enums.js", + "import": "./src/enums/ua-parser-enums.mjs" }, "./extensions": { - "require": "./src/ua-parser-extension.js", - "import": "./src/module/ua-parser-extension.mjs" + "require": "./src/extensions/ua-parser-extensions.js", + "import": "./src/extensions/ua-parser-extensions.mjs" } }, "files": [ diff --git a/script/build-dist.sh b/script/build-dist.sh index 4df2da7e8..884f4b423 100755 --- a/script/build-dist.sh +++ b/script/build-dist.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -SRC_PATH="src/ua-parser.js" +SRC_PATH="src/main/ua-parser.js" MIN_PATH="dist/ua-parser.min.js" PACK_PATH="dist/ua-parser.pack.js" diff --git a/script/build-module.js b/script/build-module.js index 4d36273e7..bbeb72cc3 100755 --- a/script/build-module.js +++ b/script/build-module.js @@ -3,19 +3,19 @@ const fs = require('fs'); const PATH = { main : { - src : 'src/ua-parser.js', - dest : 'src/module/ua-parser.mjs', + src : 'src/main/ua-parser.js', + dest : 'src/main/ua-parser.mjs', title : '' }, - enum : { - src : 'src/ua-parser-enum.js', - dest :'src/module/ua-parser-enum.mjs', - title : 'enum' + enums : { + src : 'src/enums/ua-parser-enums.js', + dest :'src/enums/ua-parser-enums.mjs', + title : 'enums' }, - extension : { - src : 'src/ua-parser-extension.js', - dest : 'src/module/ua-parser-extension.mjs', - title : 'extension' + extensions : { + src : 'src/extensions/ua-parser-extensions.js', + dest : 'src/extensions/ua-parser-extensions.mjs', + title : 'extensions' } }; const generateMJS = (module, replacers) => { @@ -30,15 +30,11 @@ const generateMJS = (module, replacers) => { fs.writeFileSync(dest, `// Generated ESM version of UAParser.js ${title} // DO NOT EDIT THIS FILE! -// Source: /src/ua-parser.js +// Source: /${src} ${text}`, 'utf-8'); }; -if (!fs.existsSync('src/module')) { - fs.mkdirSync('src/module', { recursive: true }); -} - // ua-parser.mjs generateMJS('main', [ [/\(func[\s\S]+strict\';/ig, ''], @@ -47,7 +43,7 @@ generateMJS('main', [ ]); // ua-parser-enum.mjs -generateMJS('enum', [[/module\.exports =/ig, 'export']]); +generateMJS('enums', [[/module\.exports =/ig, 'export']]); // ua-parser-extension.mjs -generateMJS('extension', [[/module\.exports =/ig, 'export']]); \ No newline at end of file +generateMJS('extensions', [[/module\.exports =/ig, 'export']]); \ No newline at end of file diff --git a/src/ua-parser-enum.js b/src/enums/ua-parser-enums.js similarity index 100% rename from src/ua-parser-enum.js rename to src/enums/ua-parser-enums.js diff --git a/src/module/ua-parser-enum.mjs b/src/enums/ua-parser-enums.mjs similarity index 98% rename from src/module/ua-parser-enum.mjs rename to src/enums/ua-parser-enums.mjs index 2081f10f5..d766e247f 100644 --- a/src/module/ua-parser-enum.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -1,6 +1,6 @@ // Generated ESM version of UAParser.js enums // DO NOT EDIT THIS FILE! -// Source: /src/ua-parser-enum.js +// Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// /* Enums for UAParser.js v2.0.0-alpha.2 diff --git a/src/ua-parser-extension.js b/src/extensions/ua-parser-extensions.js similarity index 100% rename from src/ua-parser-extension.js rename to src/extensions/ua-parser-extensions.js diff --git a/src/module/ua-parser-extension.mjs b/src/extensions/ua-parser-extensions.mjs similarity index 91% rename from src/module/ua-parser-extension.mjs rename to src/extensions/ua-parser-extensions.mjs index 44932a2c9..d0b6f8cdc 100644 --- a/src/module/ua-parser-extension.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -1,6 +1,6 @@ // Generated ESM version of UAParser.js extensions // DO NOT EDIT THIS FILE! -// Source: /src/ua-parser-extension.js +// Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// /* Extensions for UAParser.js v2.0.0-alpha.2 @@ -19,10 +19,19 @@ const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; +const Apps = Object.freeze({ + browser : [ + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']] + ] +}); + const Bots = Object.freeze({ browser : [ - // Googlebot / BingBot / MSNBot / FacebookBot - [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] + // Googlebot / BingBot / MSNBot / FacebookBot + [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + + // Slackbot - https://api.slack.com/robots + [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] ] }); @@ -111,16 +120,17 @@ const Emails = Object.freeze({ ] }); -const Tools = Object.freeze({ +const CLI = Object.freeze({ browser : [ // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']] + [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] ] }); export { + Apps, Bots, ExtraDevices, Emails, - Tools + CLI }; \ No newline at end of file diff --git a/src/ua-parser.js b/src/main/ua-parser.js similarity index 100% rename from src/ua-parser.js rename to src/main/ua-parser.js diff --git a/src/module/ua-parser.mjs b/src/main/ua-parser.mjs similarity index 98% rename from src/module/ua-parser.mjs rename to src/main/ua-parser.mjs index 250eea72a..e49eb7278 100644 --- a/src/module/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -1,6 +1,6 @@ -// Generated ESM version of UAParser.js +// Generated ESM version of UAParser.js // DO NOT EDIT THIS FILE! -// Source: /src/ua-parser.js +// Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// /* UAParser.js v2.0.0-alpha.2 @@ -734,7 +734,7 @@ // iOS/macOS /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS - /ios;fbsv\/([\d\.]+)/i, + /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i, /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, @@ -920,10 +920,10 @@ // Constructor //////////////// - function UACHData (uach, isHTTP_UACH) { + function UACHData (uach, isHttpUACH) { uach = uach || {}; setProps.call(this, CH_ALL_VALUES); - if (isHTTP_UACH) { + if (isHttpUACH) { setProps.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], @@ -1108,15 +1108,12 @@ return new UAParser(ua, extensions, headers).getResult(); } - var userAgent = ua || - ((NAVIGATOR && NAVIGATOR.userAgent) ? - NAVIGATOR.userAgent : - (headers && headers[USER_AGENT] ? - headers[USER_AGENT] : - EMPTY)), - - HTTP_UACH = new UACHData(headers, true), + var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string + ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent + (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers + EMPTY)), // empty string + httpUACH = new UACHData(headers, true), regexMap = extensions ? extend(defaultRegexes, extensions) : defaultRegexes, @@ -1124,7 +1121,7 @@ createItemFunc = function (itemType) { if (itemType == UA_RESULT) { return function () { - return new UAItem(itemType, userAgent, regexMap, HTTP_UACH) + return new UAItem(itemType, userAgent, regexMap, httpUACH) .set('ua', userAgent) .set(UA_BROWSER, this.getBrowser()) .set(UA_CPU, this.getCPU()) @@ -1135,13 +1132,13 @@ }; } else { return function () { - return new UAItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) + return new UAItem(itemType, userAgent, regexMap[itemType], httpUACH) .parseUA() .get(); }; } }; - + // public methods setProps.call(this, [ ['getBrowser', createItemFunc(UA_BROWSER)], diff --git a/test/mocha-test.js b/test/mocha-test.js index e85322dd6..682f710e0 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -4,7 +4,7 @@ var assert = require('assert'); var requirejs = require('requirejs'); var parseJS = require('@babel/parser').parse; var traverse = require('@babel/traverse').default; -var UAParser = require('../src/ua-parser'); +var UAParser = require('ua-parser-js'); var browsers = require('./specs/browser-all.json'); var cpus = require('./specs/cpu-all.json'); var devices = require('./specs/device-all.json'); @@ -159,7 +159,7 @@ describe('Testing regexes', function () { var regexes; before('Read main js file', function () { - var code = fs.readFileSync('src/ua-parser.js', 'utf8').toString(); + var code = fs.readFileSync('src/main/ua-parser.js', 'utf8').toString(); var ast = parseJS(code, { sourceType: "script" }); regexes = []; traverse(ast, { From 4c77c5ef2176401ad02fdfe05b8cd6f4653af7dc Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 24 Apr 2023 13:49:47 +0700 Subject: [PATCH 096/388] Revive the extensive list of MediaPlayers regexes by @leofiore as an Extension (Original commit reference: 3fa1fe9f704d716e8f57dea46bccc5724da7b952) --- src/extensions/ua-parser-extensions.js | 225 ++++++++++++++++++------- test/mocha-test-extension.js | 45 ++++- 2 files changed, 204 insertions(+), 66 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 329129ebd..30de6d1b0 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -24,109 +24,214 @@ const Apps = Object.freeze({ const Bots = Object.freeze({ browser : [ // Googlebot / BingBot / MSNBot / FacebookBot - [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + [/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], // Slackbot - https://api.slack.com/robots [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] ] }); +const CLIs = Object.freeze({ + browser : [ + // wget / curl / lynx + [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] + ] +}); + const ExtraDevices = Object.freeze({ - device : [ - [ - /(nook)[\w ]+build\/(\w+)/i, // Nook - /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak - /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets - /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets - /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets - /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone - ], [VENDOR, MODEL, [TYPE, TABLET]], [ + device : [[ + /(nook)[\w ]+build\/(\w+)/i, // Nook + /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak + /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets + /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets + /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets + /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(u304aa)/i // AT&T - ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ + /(u304aa)/i // AT&T + ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ - /\bsie-(\w*)/i // Siemens - ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ + /\bsie-(\w*)/i // Siemens + ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ - /\b(rct\w+) b/i // RCA Tablets - ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ + /\b(rct\w+) b/i // RCA Tablets + ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ - /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets - ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ + /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets + ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ - /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet - ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ + /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet + ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ - /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet - ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ + /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet + ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ - /\b(tm\d{3}\w+) b/i - ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ + /\b(tm\d{3}\w+) b/i + ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ - /\b(k88) b/i // ZTE K Series Tablet - ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ + /\b(k88) b/i // ZTE K Series Tablet + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ - /\b(nx\d{3}j) b/i // ZTE Nubia - ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ + /\b(nx\d{3}j) b/i // ZTE Nubia + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ - /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile - ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ + /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ - /\b(zur\d{3}) b/i // Swiss ZUR Tablet - ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ + /\b(zur\d{3}) b/i // Swiss ZUR Tablet + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ - /\b((zeki)?tb.*\b) b/i // Zeki Tablets - ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ + /\b((zeki)?tb.*\b) b/i // Zeki Tablets + ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ - /\b([yr]\d{2}) b/i, - /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet - ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [ + /\b([yr]\d{2}) b/i, + /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet + ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [ - /\b(ns-?\w{0,9}) b/i // Insignia Tablets - ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ + /\b(ns-?\w{0,9}) b/i // Insignia Tablets + ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ - /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets - ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ + /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets + ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ - /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones - ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ + /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones + ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ - /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones - ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ + /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones + ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ - /\b(ph-1) /i // Essential PH-1 - ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ + /\b(ph-1) /i // Essential PH-1 + ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ - /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets - ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ + /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets + ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ - /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets - ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ + /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets + ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ - /\btu_(1491) b/i // Rotor Tablets - ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET] - ] + /\btu_(1491) b/i // Rotor Tablets + ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]] ] }); const Emails = Object.freeze({ browser : [ - // Microsoft Outlook / Thunderbird + // Microsoft Outlook / Thunderbird [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] ] }); -const CLI = Object.freeze({ - browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] +const MediaPlayers = Object.freeze({ + browser : [[ + + /(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia + /(coremedia) v([\w\._]+)/i + ], [NAME, VERSION], [ + + /(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer + ], [NAME, VERSION], [ + + /(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy + ], [NAME, VERSION], [ + + /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i, + // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC + // NSPlayer/PSP-InternetRadioPlayer/Videos + /(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD + /(lg player|nexplayer)\s([\d\.]+)/i, + /player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player + ], [NAME, VERSION], [ + /(nexplayer)\s([\w\.-]+)/i // Nexplayer + ], [NAME, VERSION], [ + + /(flrp)\/([\w\.-]+)/i // Flip Player + ], [[NAME, 'Flip Player'], VERSION], [ + + /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i + // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit + ], [NAME], [ + + /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i + // Gstreamer + ], [NAME, VERSION], [ + + /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player + /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i, + // Java/urllib/requests/wget/cURL + /(lavf)([\d\.]+)/i // Lavf (FFMPEG) + ], [NAME, VERSION], [ + + /(htc_one_s)\/([\d\.]+)/i, // HTC One S + ], [[NAME, /_/g, ' '], VERSION], [ + + /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i, + // MPlayer SVN + ], [NAME, VERSION], [ + + /(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer + /(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN + ], [NAME, VERSION], [ + + /(mplayer)/i, // MPlayer (no other info) + /(yourmuze)/i, // YourMuze + /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime + ], [NAME], [ + + /(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout + ], [NAME, VERSION], [ + + /(nokia\d+)\/([\w\.-]+)/i // Nokia + ], [NAME, VERSION], [ + + /\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird + ], [NAME, VERSION], [ + + /(winamp)3 version ([\w\.-]+)/i, // Winamp + /(winamp)\s([\w\.-]+)/i, + /(winamp)mpeg\/([\w\.-]+)/i + ], [NAME, VERSION], [ + + /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) + // inlight radio + ], [NAME], [ + + /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i + // QuickTime/RealMedia/RadioApp/RadioClientApplication/ + // SoundTap/Totem/Stagefright/Streamium + ], [NAME, VERSION], [ + + /(smp)([\d\.]+)/i // SMP + ], [NAME, VERSION], [ + + /(vlc) media player - version ([\w\.]+)/i, // VLC Videolan + /(vlc)\/([\w\.-]+)/i, + /(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp + /(foobar2000)\/([\d\.]+)/i, // Foobar2000 + /(itunes)\/([\d\.]+)/i // iTunes + ], [NAME, VERSION], [ + + /(wmplayer)\/([\w\.-]+)/i, // Windows Media Player + /(windows-media-player)\/([\w\.-]+)/i + ], [[NAME, /-/g, ' '], VERSION], [ + + /windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i, + // Windows Media Server + ], [VERSION, [NAME, 'Windows']], [ + + /(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm + ], [NAME, VERSION], [ + + /(rad.io)\s([\d\.]+)/i, // Rad.io + /(radio.(?:de|at|fr))\s([\d\.]+)/i + ], [[NAME, 'rad.io'], VERSION] ] }); module.exports = { Apps, Bots, + CLIs, ExtraDevices, Emails, - CLI + MediaPlayers }; \ No newline at end of file diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index c97c16d26..451a99c0a 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -1,7 +1,10 @@ +const fs = require('fs'); const assert = require('assert'); -const safeRegex = require('safe-regex'); +const parseJS = require('@babel/parser').parse; +const traverse = require('@babel/traverse').default; +const safe = require('safe-regex'); const UAParser = require('ua-parser-js'); -const { Bots, Emails, CLI } = require('ua-parser-js/extensions'); +const Ext = require('ua-parser-js/extensions'); describe('Bots', () => { it('Can detect bots', () => { @@ -14,23 +17,53 @@ describe('Bots', () => { const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; - const botParser = new UAParser(Bots); + const botParser = new UAParser(Ext.Bots); assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"}); // try merging Bots & CLIs - const botsAndCLIs = { browser : [...Bots.browser, ...CLI.browser]}; + const botsAndCLIs = { browser : [...Ext.Bots.browser, ...Ext.CLIs.browser]}; const botsAndCLIsParser = new UAParser(botsAndCLIs); assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); - const emailParser = new UAParser(Emails); + const emailParser = new UAParser(Ext.Emails); assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); }); }); // TODO : move test spec to JSON file -// TODO : check for safe-regex \ No newline at end of file + +describe('Testing regexes', () => { + + let regexes; + + before('Read main js file', () => { + let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString(); + let ast = parseJS(code, { sourceType: 'script' }); + regexes = []; + traverse(ast, { + RegExpLiteral: (path) => { + regexes.push(path.node.pattern); + } + }); + if (regexes.length === 0) { + throw new Error('Regexes cannot be empty!'); + } + }); + + describe('Begin testing', () => { + it('all regexes in extension file', () => { + regexes.forEach(regex => { + describe('Test against `safe-regex` : ' + regex, () => { + it('should be safe from potentially vulnerable regex', () => { + assert.strictEqual(safe(regex), true); + }); + }); + }); + }); + }); +}); \ No newline at end of file From a74ebeb82e8cf39e3c96fd53308d2bfcac745db7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 26 Apr 2023 13:53:29 +0700 Subject: [PATCH 097/388] Only allow string for setUA() --- src/main/ua-parser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 13d5507c8..65bab0292 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1147,7 +1147,8 @@ ['getResult', createItemFunc(UA_RESULT)], ['getUA', function () { return userAgent; }], ['setUA', function (ua) { - userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; + if (typeof ua === STR_TYPE) + userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua; return this; }] ]) From 3d5c70457e588a5ab1c38d2586d55b697a2a4e8a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 27 Apr 2023 07:04:51 +0700 Subject: [PATCH 098/388] Fuzz testing using Jazzer.js --- package-lock.json | 2335 ++++++++++++++++++++++++++++++++--- package.json | 2 + test/jazzer-test-fuzzing.js | 15 + 3 files changed, 2172 insertions(+), 180 deletions(-) create mode 100644 test/jazzer-test-fuzzing.js diff --git a/package-lock.json b/package-lock.json index 2e5b36611..694dedaa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", + "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", "jshint": "~2.13.6", "mocha": "~8.2.0", @@ -36,6 +37,19 @@ "node": "*" } }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", @@ -48,6 +62,78 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/compat-data": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", + "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", + "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-compilation-targets": "^7.21.4", + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.4", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.4", + "@babel/types": "^7.21.4", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/@babel/parser": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/core/node_modules/@babel/traverse": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", + "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.4", + "@babel/types": "^7.21.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/generator": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", @@ -63,6 +149,34 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", + "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.21.4", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-function-name": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", @@ -88,6 +202,82 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-module-imports": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", + "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.4", + "@babel/types": "^7.21.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", @@ -118,6 +308,62 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/parser": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", + "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/traverse": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", + "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.4", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.4", + "@babel/types": "^7.21.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", @@ -204,119 +450,350 @@ "node": ">=6.9.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jazzer.js/core": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/core/-/core-1.4.0.tgz", + "integrity": "sha512-Wy2O6bWTOtYpfaSskYUq+iWtE6zIfp2Kang+2FvT+KT6lNMhoen/tCYZqUcMdDLgKpEqIZiDoAvMej8LgZZIxg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jazzer.js/hooking": "*", + "@jazzer.js/instrumentor": "*", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.1.5", + "tmp": "^0.2.1", + "yargs": "^17.7.1" + }, + "bin": { + "jazzer": "dist/cli.js" }, "engines": { - "node": ">=6.0.0" + "node": ">= 14.0.0", + "npm": ">= 7.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "node_modules/@jazzer.js/core/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/@jazzer.js/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=6.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "node_modules/@jazzer.js/core/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@playwright/test": { - "version": "1.32.3", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz", - "integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==", + "node_modules/@jazzer.js/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "@types/node": "*", - "playwright-core": "1.32.3" - }, - "bin": { - "playwright": "cli.js" + "color-name": "~1.1.4" }, "engines": { - "node": ">=14" - }, - "optionalDependencies": { - "fsevents": "2.3.2" + "node": ">=7.0.0" } }, - "node_modules/@types/node": { - "version": "18.15.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", - "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "node_modules/@jazzer.js/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "node_modules/@jazzer.js/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/@jazzer.js/core/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "node_modules/@jazzer.js/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@jazzer.js/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/@jazzer.js/core/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jazzer.js/core/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jazzer.js/core/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jazzer.js/core/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jazzer.js/fuzzer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/fuzzer/-/fuzzer-1.4.0.tgz", + "integrity": "sha512-GSmoMZ1KCSOJoSoxrLQRELBzQcvmfJ6+CiFWbKiSFdYc6MGr3yIpROZu77vnGUSVr8hJnDA6vffbcM9dOk5r+g==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "cmake-js": "^7.2.1", + "node-addon-api": "^6.0.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": ">= 14.0.0", + "npm": ">= 7.0.0" + } + }, + "node_modules/@jazzer.js/hooking": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/hooking/-/hooking-1.4.0.tgz", + "integrity": "sha512-HubGSnLv8wfEoj81O0dxqyaLk69QGUvDGdRkZ4QydKmVvph70lLuH3DC4O7xHdneoA5Cv37qRHGvl5whkAEYnA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.0" + }, + "engines": { + "node": ">= 14.0.0", + "npm": ">= 7.0.0" + } + }, + "node_modules/@jazzer.js/instrumentor": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/instrumentor/-/instrumentor-1.4.0.tgz", + "integrity": "sha512-DkgalciiNIPzQaVsNM1FzzdKwnMcFDaJvB8AL+byKtdz3wEj4XoGSoMDk3tJmendP59FJHaOBxSxAjPBURv2ug==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.0", + "@babel/generator": "^7.21.1", + "@jazzer.js/fuzzer": "*", + "@jazzer.js/hooking": "*", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^5.2.1", + "proper-lockfile": "^4.1.2", + "source-map-support": "^0.5.21" + }, + "engines": { + "node": ">= 14.0.0", + "npm": ">= 7.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@playwright/test": { + "version": "1.32.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz", + "integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "playwright-core": "1.32.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/@types/node": { + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "dev": true + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, "node_modules/anymatch": { @@ -332,6 +809,60 @@ "node": ">= 8" } }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -341,12 +872,49 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/axios": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz", + "integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -356,6 +924,49 @@ "node": ">=8" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -384,6 +995,64 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -393,6 +1062,26 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001481", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", + "integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -413,78 +1102,297 @@ "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==", + "dev": true, + "dependencies": { + "exit": "0.1.2", + "glob": "^7.1.1" + }, + "engines": { + "node": ">=0.2.5" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cmake-js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.2.1.tgz", + "integrity": "sha512-AdPSz9cSIJWdKvm0aJgVu3X8i0U3mNTswJkSHzZISqmYVjZk7Td4oDFg0mCBA383wO+9pG5Ix7pEP1CZH9x2BA==", + "dev": true, + "dependencies": { + "axios": "^1.3.2", + "debug": "^4", + "fs-extra": "^10.1.0", + "lodash.isplainobject": "^4.0.6", + "memory-stream": "^1.0.0", + "node-api-headers": "^0.0.2", + "npmlog": "^6.0.2", + "rc": "^1.2.7", + "semver": "^7.3.8", + "tar": "^6.1.11", + "url-join": "^4.0.1", + "which": "^2.0.2", + "yargs": "^17.6.0" + }, + "bin": { + "cmake-js": "bin/cmake-js" + }, + "engines": { + "node": ">= 14.15.0" + } + }, + "node_modules/cmake-js/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cmake-js/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cmake-js/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cmake-js/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cmake-js/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/cmake-js/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cmake-js/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cmake-js/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cmake-js/node_modules/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cmake-js/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.2" + "node": ">=8" } }, - "node_modules/cli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", - "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==", + "node_modules/cmake-js/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "exit": "0.1.2", - "glob": "^7.1.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.2.5" + "node": ">=8" } }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/cmake-js/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/cmake-js/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/cmake-js/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/cmake-js/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", "dev": true, "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/cmake-js/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, "engines": { - "node": ">=6" + "node": ">=12" } }, "node_modules/color-convert": { @@ -502,6 +1410,27 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -517,6 +1446,18 @@ "date-now": "^0.1.4" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -555,6 +1496,69 @@ "node": ">=0.10.0" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -620,91 +1624,301 @@ "domelementtype": "1" } }, + "node_modules/electron-to-chromium": { + "version": "1.4.374", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.374.tgz", + "integrity": "sha512-dNP9tQNTrjgVlSXMqGaj0BdrCS+9pcUvy5/emB6x8kh0YwCoDZ0Z4ce1+7aod+KhybHUd5o5LgKrc5al4kVmzQ==", + "dev": true + }, "node_modules/emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, - "node_modules/entities": { + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, "engines": { - "node": ">=0.8.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/gauge/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/gauge/node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "dev": true, - "bin": { - "flat": "cli.js" + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, "node_modules/get-caller-file": { "version": "2.0.5", @@ -715,6 +1929,12 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -768,6 +1988,12 @@ "node": ">=4" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -786,6 +2012,12 @@ "node": ">=4" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -795,6 +2027,12 @@ "he": "bin/he" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/htmlparser2": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", @@ -808,6 +2046,26 @@ "readable-stream": "1.1" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -824,6 +2082,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -896,6 +2160,91 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -945,6 +2294,30 @@ "jshint": "bin/jshint" } }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -966,6 +2339,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", @@ -1033,33 +2412,189 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/memory-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", + "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", + "dev": true, + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/memory-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/memory-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { - "node": "*" + "node": ">=10" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "node_modules/mocha": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", @@ -1217,6 +2752,75 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, + "node_modules/node-abi": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz", + "integrity": "sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, + "node_modules/node-api-headers": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-0.0.2.tgz", + "integrity": "sha512-YsjmaKGPDkmhoNKIpkChtCsPVaRE0a274IdERKnuc/E8K1UJdBZ4/mvI006OijlQZHCfpRNOH3dfHQs92se8gg==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1226,6 +2830,21 @@ "node": ">=0.10.0" } }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1292,6 +2911,12 @@ "node": ">=0.10.0" } }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1316,6 +2941,59 @@ "node": ">=14" } }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -1325,6 +3003,30 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", @@ -1386,6 +3088,30 @@ "node": ">=0.4.0" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1415,6 +3141,15 @@ "regexp-tree": "~0.1.1" } }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/serialize-javascript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", @@ -1430,6 +3165,76 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -1467,6 +3272,15 @@ "node": ">=4" } }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", @@ -1491,6 +3305,98 @@ "node": ">=4" } }, + "node_modules/tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -1512,6 +3418,18 @@ "node": ">=8.0" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/uglify-js": { "version": "3.12.8", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", @@ -1524,6 +3442,57 @@ "node": ">=0.8.0" } }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1621,6 +3590,12 @@ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "node_modules/yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/package.json b/package.json index dbe33f8d9..1d0c8e0ca 100644 --- a/package.json +++ b/package.json @@ -165,11 +165,13 @@ ], "scripts": { "build": "./script/build-dist.sh && ./script/build-module.js", + "fuzz": "npx jazzer ./test/jazzer-fuzz-test.js --sync", "test": "npm run build && ./script/test-all.sh" }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", + "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", "jshint": "~2.13.6", "mocha": "~8.2.0", diff --git a/test/jazzer-test-fuzzing.js b/test/jazzer-test-fuzzing.js new file mode 100644 index 000000000..c09e36f79 --- /dev/null +++ b/test/jazzer-test-fuzzing.js @@ -0,0 +1,15 @@ +const UAParser = require('ua-parser-js'); + +module.exports.fuzz = function (buffer) { + const userAgent = buffer.toString(); + const start = process.hrtime(); + UAParser(userAgent); + const elapsed = process.hrtime(start); + const milisec = (elapsed[0]*1e3+elapsed[1]*1e-6).toFixed(3); + if (milisec > 1000) { + throw new Error( + `Potential ReDoS\n` + + `Time taken: ${milisec} ms.\n` + + `User agent: ${userAgent}`); + } +}; \ No newline at end of file From 102dc51683f9901028e3c450e84acd01c6db0032 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 27 Apr 2023 12:36:52 +0700 Subject: [PATCH 099/388] Update fuzzing test --- test/{jazzer-test-fuzzing.js => jazzer-fuzz-test.js} | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) rename test/{jazzer-test-fuzzing.js => jazzer-fuzz-test.js} (67%) diff --git a/test/jazzer-test-fuzzing.js b/test/jazzer-fuzz-test.js similarity index 67% rename from test/jazzer-test-fuzzing.js rename to test/jazzer-fuzz-test.js index c09e36f79..ee30cf26e 100644 --- a/test/jazzer-test-fuzzing.js +++ b/test/jazzer-fuzz-test.js @@ -1,9 +1,14 @@ +const { FuzzedDataProvider } = require('@jazzer.js/core'); const UAParser = require('ua-parser-js'); +const UA_MAX_LENGTH = 350; module.exports.fuzz = function (buffer) { - const userAgent = buffer.toString(); + const data = new FuzzedDataProvider(buffer); + const userAgent = data.consumeString(UA_MAX_LENGTH, 'utf-8', true); const start = process.hrtime(); + UAParser(userAgent); + const elapsed = process.hrtime(start); const milisec = (elapsed[0]*1e3+elapsed[1]*1e-6).toFixed(3); if (milisec > 1000) { From a519d2b879c08d2677a1f1da488e1b71bd2f5916 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 28 Apr 2023 07:06:45 +0700 Subject: [PATCH 100/388] Add new Action: publish to NPM --- .github/workflows/publish-github-packages.yml | 20 +++------------- .github/workflows/publish-npm-packages.yml | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/publish-npm-packages.yml diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml index eaea41c37..25f1ca8f2 100644 --- a/.github/workflows/publish-github-packages.yml +++ b/.github/workflows/publish-github-packages.yml @@ -1,7 +1,4 @@ -# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created -# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages - -name: Publish GitHub Package +name: Publish to GitHub Package on: release: @@ -9,17 +6,6 @@ on: jobs: build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 16 - - run: npm ci - - run: npm test - - publish-gpr: - needs: build runs-on: ubuntu-latest permissions: contents: read @@ -28,9 +14,9 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: '18.x' registry-url: https://npm.pkg.github.com/ - run: npm ci - run: npm publish env: - NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml new file mode 100644 index 000000000..b4f35d974 --- /dev/null +++ b/.github/workflows/publish-npm-packages.yml @@ -0,0 +1,23 @@ +name: Publish to NPM + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18.x' + registry-url: 'https://registry.npmjs.org' + - run: npm install -g npm + - run: npm ci + - run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file From df9144b49322c705e925b81e0947887f9bb69ec5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 6 May 2023 19:53:51 +0700 Subject: [PATCH 101/388] Modify test scripts --- package.json | 8 ++++++-- script/test-all.sh | 13 +++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1d0c8e0ca..9913e2e9d 100644 --- a/package.json +++ b/package.json @@ -165,8 +165,12 @@ ], "scripts": { "build": "./script/build-dist.sh && ./script/build-module.js", - "fuzz": "npx jazzer ./test/jazzer-fuzz-test.js --sync", - "test": "npm run build && ./script/test-all.sh" + "fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync", + "test": "./script/test-all.sh", + "test:jshint": "jshint src && jshint script", + "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", + "test:mocha": "mocha -R list test/mocha*js", + "test:playwright": "playwright test" }, "devDependencies": { "@babel/parser": "7.15.8", diff --git a/script/test-all.sh b/script/test-all.sh index b77df1a85..b47652bda 100755 --- a/script/test-all.sh +++ b/script/test-all.sh @@ -1,21 +1,26 @@ #!/usr/bin/env bash +echo ' +- run build +' +npm run build || exit 1 + echo ' - lint js code ' -jshint src && jshint script || exit 1 +npm run test:jshint || exit 1 echo ' - test using mocha ' -mocha -R list test/mocha*js || exit 1 +npm run test:mocha || exit 1 echo ' - test using playwright ' -npx playwright test || exit 1 +npm run test:playwright || exit 1 echo ' - lint lockfile ' -npx lockfile-lint -p package-lock.json || exit 1 \ No newline at end of file +npm run test:lockfile-lint || exit 1 \ No newline at end of file From 15d17e97a15d2aafdded34a5b094014e9b1c01f5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 6 May 2023 21:21:13 +0700 Subject: [PATCH 102/388] Add some tests; Add new devices: Infinix, Tecno; Improve detection: Xiaomi POCO Source: https://www.useragents.me --- src/main/ua-parser.js | 4 +- test/specs/browser-all.json | 100 ++++++++ test/specs/cpu-all.json | 40 +++ test/specs/device-all.json | 492 ++++++++++++++++++++++++++++++------ 4 files changed, 552 insertions(+), 84 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 65bab0292..06a03db3c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -474,7 +474,7 @@ ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ // Xiaomi - /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO + /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi @@ -582,7 +582,7 @@ ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [ // MIXED - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i, + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron /(hp) ([\w ]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i, // Asus diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 9ca190c61..507a9df37 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -199,6 +199,46 @@ "major" : "100" } }, + { + "desc" : "Chrome 112.0 on Win10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome", + "version" : "112.0.0.0", + "major" : "112" + } + }, + { + "desc" : "Chrome 112.0 on macOS", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome", + "version" : "112.0.0.0", + "major" : "112" + } + }, + { + "desc" : "Chrome 111.0 on Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome", + "version" : "111.0.0.0", + "major" : "111" + } + }, + { + "desc" : "Chrome 111.0 on ChromeOS", + "ua" : "Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome", + "version" : "111.0.0.0", + "major" : "111" + } + }, { "desc" : "Chrome Headless", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome Safari/537.36", @@ -1328,6 +1368,56 @@ "major" : "22" } }, + { + "desc" : "Yandex", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 YaBrowser/23.3.0.2246 Yowser/2.5 Safari/537.36", + "expect" : + { + "name" : "Yandex", + "version" : "23.3.0.2246", + "major" : "23" + } + }, + { + "desc" : "Yandex on Android", + "ua" : "Mozilla/5.0 (Linux; arm_64; Android 13; SM-G965F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 YaBrowser/21.3.4.59 Mobile Safari/537.36", + "expect" : + { + "name" : "Yandex", + "version" : "21.3.4.59", + "major" : "21" + } + }, + { + "desc" : "Yandex on iPhone", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/604.1", + "expect" : + { + "name" : "Yandex", + "version" : "23.3.3.330", + "major" : "23" + } + }, + { + "desc" : "Yandex on iPad", + "ua" : "Mozilla/5.0 (iPad; CPU OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/605.1", + "expect" : + { + "name" : "Yandex", + "version" : "23.3.3.330", + "major" : "23" + } + }, + { + "desc" : "Yandex on iPod", + "ua" : "Mozilla/5.0 (iPod touch; CPU iPhone 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/605.1", + "expect" : + { + "name" : "Yandex", + "version" : "23.3.3.330", + "major" : "23" + } + }, { "desc" : "Puffin", "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo P2a42 Build/MMB29M; en-us) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Puffin/6.0.8.15804AP", @@ -1428,6 +1518,16 @@ "major" : "1" } }, + { + "desc" : "Firefox on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/112.0 Mobile/15E148 Safari/605.1.15", + "expect" : + { + "name" : "Mobile Firefox", + "version" : "112.0", + "major" : "112" + } + }, { "desc" : "Firefox iOS using iPad", "ua" : "Mozilla/5.0 (iPad; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4", diff --git a/test/specs/cpu-all.json b/test/specs/cpu-all.json index 2a7d18920..da86557f0 100644 --- a/test/specs/cpu-all.json +++ b/test/specs/cpu-all.json @@ -23,6 +23,46 @@ "architecture" : "amd64" } }, + { + "desc" : "Vivaldi on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18", + "expect" : + { + "architecture" : "amd64" + } + }, + { + "desc" : "Vivaldi on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18", + "expect" : + { + "architecture" : "amd64" + } + }, + { + "desc" : "Vivaldi on Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18", + "expect" : + { + "architecture" : "amd64" + } + }, + { + "desc" : "Vivaldi on Linux", + "ua" : "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18", + "expect" : + { + "architecture" : "ia32" + } + }, + { + "desc": "Xiaomi POCO M2 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; POCO M2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.7.42.00 SA/3 Mobile Safari/537.36", + "expect" : + { + "architecture" : "arm64" + } + }, { "desc" : "win64", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C)", diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 1c54884c0..6a96d800e 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1,4 +1,14 @@ -[{ +[ + { + "desc": "K", + "ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "K", + "type": "mobile" + } + }, + { "desc": "ASUS Nexus 7", "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", "expect": { @@ -187,6 +197,15 @@ "type": "mobile" } }, + { + "desc": "Blackview 4900Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; BV4900Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "BV4900Pro", + "type": "mobile" + } + }, { "desc": "Desktop (IE11 with Tablet string)", "ua": "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; GWX:MANAGED; rv:11.0) like Gecko", @@ -413,31 +432,31 @@ } }, { - "desc": "Huawei Mate 20 X", - "ua": "Mozilla/5.0 (Linux; Android 9; EVR-L29 Build/HUAWEIEVR-L29; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "EVR-L29", - "type": "mobile" - } + "desc": "Huawei Mate 20 X", + "ua": "Mozilla/5.0 (Linux; Android 9; EVR-L29 Build/HUAWEIEVR-L29; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "EVR-L29", + "type": "mobile" + } }, { - "desc": "Huawei Mate 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LYA-L09", - "type": "mobile" - } + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-L09", + "type": "mobile" + } }, { - "desc": "Huawei Mate 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LYA-AL00", - "type": "mobile" - } + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-AL00", + "type": "mobile" + } }, { "desc": "Huawei Mate 20 Pro", @@ -447,8 +466,8 @@ "model": "LYA-AL10", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Mate 20 Pro", "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L0C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", "expect": { @@ -456,8 +475,8 @@ "model": "LYA-L0C", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Mate 20 Pro", "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", "expect": { @@ -465,8 +484,8 @@ "model": "LYA-L29", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Mate 20 Pro", "ua": "Mozilla/5.0 (Linux; Android 9; LYA-TL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", "expect": { @@ -475,6 +494,15 @@ "type": "mobile" } }, + { + "desc": "Huawei Mate 50 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; DCO-LX9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DCO-LX9", + "type": "mobile" + } + }, { "desc": "Huawei P20 Lite", "ua": "Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36", @@ -485,13 +513,13 @@ } }, { - "desc": "Huawei P20", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; EML-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "EML-L29", - "type": "mobile" - } + "desc": "Huawei P20", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; EML-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "EML-L29", + "type": "mobile" + } }, { "desc": "Huawei P20 Pro", @@ -564,8 +592,8 @@ "model": "YAL-L21", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Nova 5T", "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", "expect": { @@ -573,8 +601,8 @@ "model": "YAL-L61", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Nova 5T", "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L71) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", "expect": { @@ -582,8 +610,8 @@ "model": "YAL-L71", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Nova 5T", "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", "expect": { @@ -591,8 +619,8 @@ "model": "YAL-L61D", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Nova 5T", "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", "expect": { @@ -600,8 +628,8 @@ "model": "YALE-L61A", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Nova 5T", "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", "expect": { @@ -609,8 +637,8 @@ "model": "YALE-L61D", "type": "mobile" } - }, - { + }, + { "desc": "Huawei Nova 5T", "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L71A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", "expect": { @@ -745,6 +773,51 @@ "type": "mobile" } }, + { + "desc": "Infinix Hot 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; Infinix X625C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X625C", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 10T", + "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X689C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X689C", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 11s", + "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X6812 Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X6812", + "type": "mobile" + } + }, + { + "desc": "Infinix Smart 5", + "ua": "Mozilla/5.0 (Linux; Android 10; Infinix X657C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X657C", + "type": "mobile" + } + }, + { + "desc": "Infinix Zero 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; Infinix X6815B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X6815B", + "type": "mobile" + } + }, { "desc": "Apple Desktop", "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15", @@ -925,6 +998,24 @@ "type": "mobile" } }, + { + "desc": "LG K40", + "ua": "Mozilla/5.0 (Linux; Android 10; LM-X420) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-X420", + "type": "mobile" + } + }, + { + "desc": "LG Stylo 4", + "ua": "Mozilla/5.0 (Linux; Android 10; LM-Q710(FGN)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "LM-Q710(FGN)", + "type": "mobile" + } + }, { "desc": "LG Stylo 5", "ua": "Mozilla/5.0 (Linux; Android 9; LM-Q720) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", @@ -943,6 +1034,15 @@ "type": "mobile" } }, + { + "desc": "LG K20", + "ua": "Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:111.0) Gecko/111.0 Firefox/111.0", + "expect": { + "vendor": "LG", + "model": "M255", + "type": "mobile" + } + }, { "desc": "LG K500", "ua": "Mozilla/5.0 (Linux; Android 6.0.1; LG-K500 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36", @@ -1061,23 +1161,30 @@ } }, { - "desc" : "Meizu M3S", - "ua" : "Mozilla/5.0 (X11; Linux; Android 5.1; MZ-M3s Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrom/45.0.2454.94 Mobile Safari/537.36", - "expect" : - { - "vendor" : "Meizu", - "model" : "M3s", - "type" : "mobile" + "desc": "Motorola Moto Z3 Play", + "ua": "Mozilla/5.0 (Linux; Android 9; Moto Z3 Play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto Z3 Play", + "type": "mobile" } }, { - "desc" : "Microsoft Lumia 950", - "ua" : "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", - "expect" : - { - "vendor" : "Microsoft", - "model" : "Lumia 950", - "type" : "mobile" + "desc": "Meizu M3S", + "ua": "Mozilla/5.0 (X11; Linux; Android 5.1; MZ-M3s Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrom/45.0.2454.94 Mobile Safari/537.36", + "expect": { + "vendor": "Meizu", + "model": "M3s", + "type": "mobile" + } + }, + { + "desc": "Microsoft Lumia 950", + "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", + "expect": { + "vendor": "Microsoft", + "model": "Lumia 950", + "type": "mobile" } }, { @@ -1215,6 +1322,15 @@ "type": "mobile" } }, + { + "desc": "Nokia 7", + "ua": "Mozilla/5.0 (Linux; Android 11; Nokia 7.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "7.2", + "type": "mobile" + } + }, { "desc": "Nokia N9", "ua": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", @@ -1314,6 +1430,15 @@ "type": "mobile" } }, + { + "desc": "OnePlus 7T Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36 EdgA/110.0.1587.66", + "expect": { + "vendor": "undefined", + "model": "HD1913", + "type": "mobile" + } + }, { "desc": "OnePlus 8T", "ua": "Mozilla/5.0 (Linux; Android 11; KB2005) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", @@ -1332,6 +1457,15 @@ "type": "mobile" } }, + { + "desc": "OnePlus 10RT", + "ua": "Mozilla/5.0 (Linux; Android 13; CPH2413) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH2413", + "type": "mobile" + } + }, { "desc": "OnePlus Nord N100", "ua": "Mozilla/5.0 (Linux; Android 10; BE2015 Build/QKQ1.200719.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36", @@ -1494,6 +1628,33 @@ "type": "mobile" } }, + { + "desc": "Realme 3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; RMX1851) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1851", + "type": "mobile" + } + }, + { + "desc": "Realme 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; RMX3471) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "RMX3471", + "type": "mobile" + } + }, + { + "desc": "Realme GT Master", + "ua": "Mozilla/5.0 (Linux; Android 13; RMX3363) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "RMX3363", + "type": "mobile" + } + }, { "desc": "Panasonic T31", "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Panasonic T31 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Mobile Safari/537.36 ", @@ -1683,6 +1844,24 @@ "type": "mobile" } }, + { + "desc": "Samsung Galaxy A50s", + "ua": "Mozilla/5.0 (Linux; Android 11; SM-A507FN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A507FN", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A52s", + "ua": "Mozilla/5.0 (Linux; Android 13; SM-A528B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A528B", + "type": "mobile" + } + }, { "desc": "Samsung Galaxy A80", "ua": "Mozilla/5.0 (Linux; Android 9; SM-A805F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.112 Mobile Safari/537.36", @@ -1736,7 +1915,7 @@ "model": "SCG01", "type": "mobile" } - }, + }, { "desc": "Samsung Galaxy Note 10+", "ua": "Mozilla/5.0 (Linux; Android 9; SM-N976V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36", @@ -1755,6 +1934,15 @@ "type": "mobile" } }, + { + "desc": "Samsung C8", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; SM-C7108) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-C7108", + "type": "mobile" + } + }, { "desc": "Samsung Galaxy Note 8", "ua": "Mozilla/5.0 (Linux; Android 4.2.2; GT-N5100 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Safari/537.36", @@ -1840,9 +2028,9 @@ "desc": "Samsung Note 10.1", "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-P605) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", "expect": { - "vendor": "Samsung", - "model": "SM-P605", - "type": "tablet" + "vendor": "Samsung", + "model": "SM-P605", + "type": "tablet" } }, { @@ -1953,6 +2141,15 @@ "type": "mobile" } }, + { + "desc": "Sony G8141 (Xperia XZ1)", + "ua": "Mozilla/5.0 (Linux; Android 9; SO-01K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SO-01K", + "type": "mobile" + } + }, { "desc": "Sony G8141 (Xperia XZ Premium)", "ua": "Mozilla/5.0 (Linux; Android 8.0.0; G8141) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36", @@ -2116,40 +2313,45 @@ } }, { - "desc" : "Tesla", - "ua" : "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1", - "expect" : - { + "desc": "Tecno KC8", + "ua": "Mozilla/5.0 (Linux; Android 10; TECNO KC8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "KC8", + "type": "mobile" + } + }, + { + "desc": "Tesla", + "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1", + "expect": { "vendor": "Tesla", "model": "undefined", "type": "embedded" } }, { - "desc" : "Tesla", - "ua" : "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.16.2.1-e99c70fff409", - "expect" : - { + "desc": "Tesla", + "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.16.2.1-e99c70fff409", + "expect": { "vendor": "Tesla", "model": "undefined", "type": "embedded" } }, { - "desc" : "TechniSAT Digit ISIO S SAT receiver", - "ua" : "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50", - "expect" : - { + "desc": "TechniSAT Digit ISIO S SAT receiver", + "ua": "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50", + "expect": { "vendor": "TechniSat", "model": "Digit ISIO S", "type": "smarttv" } }, { - "desc" : "TechniSAT MultyVision SmartTV", - "ua" : "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50", - "expect" : - { + "desc": "TechniSAT MultyVision SmartTV", + "ua": "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50", + "expect": { "vendor": "TechniSat", "model": "MultyVision ISIO", "type": "smarttv" @@ -2317,6 +2519,51 @@ "type": "mobile" } }, + { + "desc": "Xiaomi POCO X3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; M2102J20SI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2102J20SI", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO X3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; M2102J20SG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2102J20SG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO X3 NFC", + "ua": "Mozilla/5.0 (Linux; Android 12; M2007J20CG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2007J20CG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO M2 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; POCO M2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.7.42.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "POCO M2 Pro", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO M3", + "ua": "Mozilla/5.0 (Linux; Android 10; M2010J19CI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2010J19CI", + "type": "mobile" + } + }, { "desc": "Xiaomi Redmi 4A", "ua": "Mozilla/5.0 (Linux; Android 6.0; Redmi 4A Build/MMB29M; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", @@ -2326,6 +2573,15 @@ "type": "mobile" } }, + { + "desc": "Xiaomi Redmi 10C", + "ua": "Mozilla/5.0 (Linux; Android 12; 220333QAG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "220333QAG", + "type": "mobile" + } + }, { "desc": "Xiaomi Redmi K30 5G", "ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", @@ -2362,6 +2618,51 @@ "type": "mobile" } }, + { + "desc": "XiaoMi Redmi Note 9S", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 9S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 9S", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; M2103K19C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "M2103K19C", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "M2101K6P", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; M2101K6G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "M2101K6G", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 8", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 8", + "type": "mobile" + } + }, { "desc": "PlayStation 4", "ua": "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", @@ -2461,6 +2762,15 @@ "type": "mobile" } }, + { + "desc": "Samsung Galaxy J7 Prime", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; SM-G610F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G610F", + "type": "mobile" + } + }, { "desc": "Samsung Galaxy S6", "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-G920I Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", @@ -2907,6 +3217,15 @@ "type": "mobile" } }, + { + "desc": "Vivo Y93", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; vivo 1814) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1814", + "type": "mobile" + } + }, { "desc": "Vivo Y97", "ua": "Mozilla/5.0 (Linux; Android 8.1.0; V1813T Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.0.14.0", @@ -2925,6 +3244,15 @@ "type": "mobile" } }, + { + "desc": "Vivo 1906 (Y11)", + "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1906) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1906", + "type": "mobile" + } + }, { "desc": "Unknown Mobile using Firefox", "ua": "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0", @@ -3006,4 +3334,4 @@ "type": "mobile" } } -] +] \ No newline at end of file From b20d09655cf01657d00e38e334a757c25b176175 Mon Sep 17 00:00:00 2001 From: Runar Heggset Date: Wed, 3 May 2023 11:28:54 +0200 Subject: [PATCH 103/388] Fix Amazon Fire TV device detection --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 65bab0292..45dddb000 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -627,7 +627,7 @@ ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ /crkey/i // Google Chromecast ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ - /droid.+aft(\w)( bui|\))/i // Fire TV + /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ /\(dtv[\);].+(aquos)/i, /(aquos-tv[\w ]+)\)/i // Sharp diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 1c54884c0..b43969c41 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2772,6 +2772,15 @@ "type": "smarttv" } }, + { + "desc": "Amazon Fire TV", + "ua": "Mozilla/5.0 (Linux; Android 9; AFTKA Build/PS7633.3445N; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.160 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KA", + "type": "smarttv" + } + }, { "desc": "Android TV", "ua": "Mozilla/5.0 (Linux; Android 10; 2020/2021 UHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36", From 072a82b87ba35cc15ae10fd02d905f4b78a8ce05 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 27 May 2023 20:45:43 +0700 Subject: [PATCH 104/388] Add a new submodule: `ua-parser-helpers` with a method: `isFrozenUA()` to match a string with a frozen user-agent pattern --- package.json | 4 +++ script/build-module.js | 10 +++++++- src/helpers/ua-parser-helpers.js | 42 ++++++++++++++++++++++++++++++++ test/mocha-test-helpers.js | 35 ++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/helpers/ua-parser-helpers.js create mode 100644 test/mocha-test-helpers.js diff --git a/package.json b/package.json index 9913e2e9d..ea8379f8c 100644 --- a/package.json +++ b/package.json @@ -157,6 +157,10 @@ "./extensions": { "require": "./src/extensions/ua-parser-extensions.js", "import": "./src/extensions/ua-parser-extensions.mjs" + }, + "./helpers": { + "require": "./src/helpers/ua-parser-helpers.js", + "import": "./src/helpers/ua-parser-helpers.mjs" } }, "files": [ diff --git a/script/build-module.js b/script/build-module.js index bbeb72cc3..d7f5a59ae 100755 --- a/script/build-module.js +++ b/script/build-module.js @@ -16,6 +16,11 @@ const PATH = { src : 'src/extensions/ua-parser-extensions.js', dest : 'src/extensions/ua-parser-extensions.mjs', title : 'extensions' + }, + helpers : { + src : 'src/helpers/ua-parser-helpers.js', + dest : 'src/helpers/ua-parser-helpers.mjs', + title : 'helpers' } }; const generateMJS = (module, replacers) => { @@ -46,4 +51,7 @@ generateMJS('main', [ generateMJS('enums', [[/module\.exports =/ig, 'export']]); // ua-parser-extension.mjs -generateMJS('extensions', [[/module\.exports =/ig, 'export']]); \ No newline at end of file +generateMJS('extensions', [[/module\.exports =/ig, 'export']]); + +// ua-parser-helpers.mjs +generateMJS('helpers', [[/module\.exports =/ig, 'export']]); \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js new file mode 100644 index 000000000..9e6c3395a --- /dev/null +++ b/src/helpers/ua-parser-helpers.js @@ -0,0 +1,42 @@ +/////////////////////////////////////////////// +/* Helpers for UAParser.js v2.0.0-alpha.2 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +/* + # Reference: + https://www.chromium.org/updates/ua-reduction/ + + # Desktop + --- + Format: + Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 + + Possible values: + - Windows NT 10.0; Win64; x64 + - Macintosh; Intel Mac OS X 10_15_7 + - X11; Linux x86_64 + - X11; CrOS x86_64 14541.0.0 + - Fuchsia + + # Mobile & Tablet: (except iOS/Android WebView) + --- + Format: + Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 + + Possible values: + - "Mobile" + - "" (empty string for Tablets & Desktop) +*/ + +const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/; + +const isFrozenUA = str => frozenUA.test(str); + +module.exports = { + isFrozenUA +}; \ No newline at end of file diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js new file mode 100644 index 000000000..b954e73cc --- /dev/null +++ b/test/mocha-test-helpers.js @@ -0,0 +1,35 @@ +const { isFrozenUA } = require('ua-parser-js/helpers'); +const assert = require('assert'); + +describe('isFrozenUA', () => { + it('Returns whether a user agent is frozen', () => { + + const regularWindowsUA = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36"; + const freezedWindowsUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36"; + + const regularMacUA = ""; + const freezedMacUA = ""; + + const regularLinuxUA = ""; + const freezedLinuxUA = ""; + + const regularCrOSUA = ""; + const freezedCrOSUA = ""; + + const regularFuchsiaUA = ""; + const freezedFuchsiaUA = ""; + + const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36"; + const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36"; + + const regularTabletUA = "Mozilla/5.0 (Linux; Android 9; SM-T810) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36"; + const freezedTabletUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36"; + + assert.strictEqual(isFrozenUA(regularWindowsUA), false); + assert.strictEqual(isFrozenUA(freezedWindowsUA), true); + assert.strictEqual(isFrozenUA(regularMobileUA), false); + assert.strictEqual(isFrozenUA(freezedMobileUA), true); + assert.strictEqual(isFrozenUA(regularTabletUA), false); + assert.strictEqual(isFrozenUA(freezedTabletUA), true); + }); +}); \ No newline at end of file From 153831d2edb73db1f066c5638da733d302027a1e Mon Sep 17 00:00:00 2001 From: JBYoshi <12983479+JBYoshi@users.noreply.github.com> Date: Fri, 2 Jun 2023 22:05:01 -0500 Subject: [PATCH 105/388] Add Snapchat user agent. --- src/main/ua-parser.js | 2 +- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 06a03db3c..211b2ec89 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -347,7 +347,7 @@ /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android - /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram + /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA']], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 507a9df37..2ef402e6f 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1869,5 +1869,15 @@ "version" : "41.0", "major" : "41" } + }, + { + "desc" : "Snapchat", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Snapchat/12.33.0.36 (like Safari/8614.1.25.0.31, panda)", + "expect" : + { + "name" : "Snapchat", + "version" : "12.33.0.36", + "major" : "12" + } } ] \ No newline at end of file From f2e4b242ceaa9bcdafd731066fe665c2b28132f5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 7 Aug 2023 21:35:46 +0700 Subject: [PATCH 106/388] Update sponsors section in readme --- readme.md | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/readme.md b/readme.md index 7b669d966..43d66e94a 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,9 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro * Author : Faisal Salman <> * Demo : https://faisalman.github.io/ua-parser-js * Source : https://github.com/faisalman/ua-parser-js -* Documentation : https://faisalman.github.io/ua-parser-js-docs/v2 +* Documentation : + * v1 : https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation + * v2 : https://faisalman.github.io/ua-parser-js-docs/v2 *** @@ -30,14 +32,6 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro -51degrees -↗ @51degrees/ua-parser-js - - -

UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.

This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).

Visit ↗ 51Degrees UAParser to get started.

- - - ↗ Become a sponsor @@ -129,8 +123,9 @@ NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb, Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris, Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, [Mobile] Safari, Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim, -SlimBrowser, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser, UP.Browser, Viera, -Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ... +SlimBrowser, Snapchat, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser, +UP.Browser, Viera, Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, +Whale Browser, ... # 'browser.version' determined dynamically ``` @@ -152,10 +147,11 @@ console, mobile, tablet, smarttv, wearable, embedded # Possible 'device.vendor': Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell, -Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Kobo, -Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus, -OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp, -Siemens, Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ... +Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Infinix, Jolla, +Kobo, Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, +OnePlus, OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, +Sharp, Siemens, Sony[Ericsson], Sprint, Tecno, Tesla, Vivo, Vodafone, Xbox, Xiaomi, +Zebra, ZTE, ... # 'device.model' determined dynamically ``` From 22eae9f70cbbecf4d425c5195081657b01bf1274 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 7 Aug 2023 21:39:21 +0700 Subject: [PATCH 107/388] [extensions] Add GPTBot to Bots --- src/extensions/ua-parser-extensions.js | 3 +++ test/mocha-test-extension.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 30de6d1b0..b7ce27ec8 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -26,6 +26,9 @@ const Bots = Object.freeze({ // Googlebot / BingBot / MSNBot / FacebookBot [/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + // GPTBot - https://platform.openai.com/docs/gptbot + [/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + // Slackbot - https://api.slack.com/robots [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] ] diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 451a99c0a..e83ad14e1 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -9,6 +9,7 @@ const Ext = require('ua-parser-js/extensions'); describe('Bots', () => { it('Can detect bots', () => { const googleBot = 'Googlebot-Video/1.0'; + const gptBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)'; const msnBot = 'msnbot-media/1.1 (+http://search.msn.com/msnbot.htm)'; const bingPreview = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b'; const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)'; @@ -19,6 +20,7 @@ describe('Bots', () => { const botParser = new UAParser(Ext.Bots); assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"}); From 420bf1066c02bcf9d4243a9e0c9da8acf00b9811 Mon Sep 17 00:00:00 2001 From: chenyuan-new <53860479+chenyuan-new@users.noreply.github.com> Date: Tue, 8 Aug 2023 16:22:10 +0800 Subject: [PATCH 108/388] fix: remove duplicated BRANDS input when call setProps in UACHData func (#663) --- src/main/ua-parser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 45dddb000..a8ed359e7 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -925,7 +925,6 @@ setProps.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], - [BRANDS, itemListToArray(uach[CH_HEADER])], [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], [MODEL, stripQuotes(uach[CH_HEADER_MODEL])], [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], From f76d8983ca08a83e7c39a7d5ec8bf1ee8ffdff98 Mon Sep 17 00:00:00 2001 From: Runar Heggset Date: Wed, 3 May 2023 11:28:54 +0200 Subject: [PATCH 109/388] Fix Amazon Fire TV device detection --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 211b2ec89..56aaf58b1 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -627,7 +627,7 @@ ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ /crkey/i // Google Chromecast ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ - /droid.+aft(\w)( bui|\))/i // Fire TV + /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ /\(dtv[\);].+(aquos)/i, /(aquos-tv[\w ]+)\)/i // Sharp diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 6a96d800e..093faa92a 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -3082,6 +3082,15 @@ "type": "smarttv" } }, + { + "desc": "Amazon Fire TV", + "ua": "Mozilla/5.0 (Linux; Android 9; AFTKA Build/PS7633.3445N; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.160 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KA", + "type": "smarttv" + } + }, { "desc": "Android TV", "ua": "Mozilla/5.0 (Linux; Android 10; 2020/2021 UHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36", From 3cb567c1541c958e23f94a0c73aeaf4eab0306c7 Mon Sep 17 00:00:00 2001 From: chenyuan-new <53860479+chenyuan-new@users.noreply.github.com> Date: Tue, 8 Aug 2023 16:22:10 +0800 Subject: [PATCH 110/388] fix: remove duplicated BRANDS input when call setProps in UACHData func (#663) --- src/main/ua-parser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 56aaf58b1..861bb105b 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -925,7 +925,6 @@ setProps.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], - [BRANDS, itemListToArray(uach[CH_HEADER])], [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], [MODEL, stripQuotes(uach[CH_HEADER_MODEL])], [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], From 6e26e38247f35e31558daabaf608620eb2451b8a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 14 Aug 2023 12:28:36 +0700 Subject: [PATCH 111/388] [extension] Add Axios, jsdom, Scrapy. Also fixes #627 #156 #217 #227 Axios: `axios/VERSION` https://www.zenrows.com/blog/axios-user-agent#what-is-axios-user-agent JSDOM: `Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${jsdomVersion}` https://github.com/jsdom/jsdom Scrapy: `Scrapy/VERSION (+https://scrapy.org)` https://docs.scrapy.org/en/master/topics/settings.html --- src/extensions/ua-parser-extensions.js | 10 +++++++++- test/mocha-test-extension.js | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index b7ce27ec8..3ace7c1bb 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -230,11 +230,19 @@ const MediaPlayers = Object.freeze({ ] }); +const Modules = Object.freeze({ + browser : [ + // Axios/jsdom/Scrapy + [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']] + ] +}); + module.exports = { Apps, Bots, CLIs, ExtraDevices, Emails, - MediaPlayers + MediaPlayers, + Modules }; \ No newline at end of file diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index e83ad14e1..605bf6389 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -17,6 +17,9 @@ describe('Bots', () => { const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)'; const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; + const axios = 'axios/1.3.5'; + const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; + const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; const botParser = new UAParser(Ext.Bots); assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); @@ -34,6 +37,11 @@ describe('Bots', () => { const emailParser = new UAParser(Ext.Emails); assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); + + const moduleParser = new UAParser(Ext.Modules); + assert.deepEqual(moduleParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "module"}); + assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"}); + assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"}); }); }); From d168b75a3a21d9d3cb1292fc7a2e36738468fb7d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 17 Aug 2023 11:29:18 +0700 Subject: [PATCH 112/388] Bump version 2.0.0-alpha.3 --- bower.json | 2 +- changelog.md | 7 + dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- images/51degrees.svg | 19 -- package-lock.json | 4 +- package.js | 2 +- package.json | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 236 ++++++++++++++++++------ src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 46 +++++ src/main/ua-parser.js | 14 +- src/main/ua-parser.mjs | 26 +-- 16 files changed, 262 insertions(+), 112 deletions(-) delete mode 100644 images/51degrees.svg create mode 100644 src/helpers/ua-parser-helpers.mjs diff --git a/bower.json b/bower.json index 0f5d520fd..a8c0ae384 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ua-parser-js", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.3", "authors": [ "Faisal Salman " ], diff --git a/changelog.md b/changelog.md index ac8e493be..5339d6cb7 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,13 @@ - Add support for ES module `import { UAParser } from 'ua-parser-js'` - Provide Enums `'ua-parser-js/enums'` - Provide Extensions `'ua-parser-js/extensions'` + - Provide Helpers `'ua-parser-js/helpers'` + +## Version 2.0.0-alpha.3 +- Add `withFeatureCheck()` method +- Add `isFrozenUA()` method in `helpers` submodule +- Add `MediaPlayers` & `Modules` in `extensions` submodule +- Fix issue with ESM import ## Version 2.0.0-alpha.2 - Fix browser result always returning Chromium when using `withClientHints()` diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index e18ba06a7..46cf21f2f 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-alpha.2 +/* UAParser.js v2.0.0-alpha.3 Copyright © 2012-2023 Faisal Salman MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-bitness",CH_HEADER_MOBILE=CH_HEADER+"-mobile",CH_HEADER_MODEL=CH_HEADER+"-model",CH_HEADER_PLATFORM=CH_HEADER+"-platform",CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=["brands","fullVersionList",MOBILE,MODEL,"platform","platformVersion",ARCHITECTURE,"bitness"],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=ua||(NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY),HTTP_UACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,HTTP_UACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],HTTP_UACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){userAgent=typeof ua===STR_TYPE&&ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(typeof ua===STR_TYPE)userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 59ce392a0..0ba28a22b 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-alpha.2 +/* UAParser.js v2.0.0-alpha.3 Copyright © 2012-2023 Faisal Salman MIT License */ -!function(i,u){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i,this}]]).setUA(o),this}Ni.VERSION="2.0.0-alpha.2",Ni.BROWSER=e([h,m,l]),Ni.CPU=e([g]),Ni.DEVICE=e([p,o,f,r,v,x,a,k,y]),Ni.ENGINE=Ni.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ni),exports.UAParser=Ni):typeof define===c&&define.amd?define(function(){return Ni}):typeof i!==b&&(i.UAParser=Ni);var Oi,zi=typeof i!==b&&(i.jQuery||i.Zepto);zi&&!zi.ua&&(Oi=new Ni,zi.ua=Oi.getResult(),zi.ua.get=function(){return Oi.getUA()},zi.ua.set=function(i){Oi.setUA(i);var e,t=Oi.getResult();for(e in t)zi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,c){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i),this}]]).setUA(o),this}zi.VERSION="2.0.0-alpha.3",zi.BROWSER=e([h,m,u]),zi.CPU=e([g]),zi.DEVICE=e([p,o,f,r,v,x,a,k,y]),zi.ENGINE=zi.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===l&&define.amd?define(function(){return zi}):typeof i!==b&&(i.UAParser=zi);var Ni,Oi=typeof i!==b&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ni=new zi,Oi.ua=Ni.getResult(),Oi.ua.get=function(){return Ni.getUA()},Oi.ua.set=function(i){Ni.setUA(i);var e,t=Ni.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/images/51degrees.svg b/images/51degrees.svg deleted file mode 100644 index c3393f646..000000000 --- a/images/51degrees.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - diff --git a/package-lock.json b/package-lock.json index 694dedaa2..f0d514f4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.3", "funding": [ { "type": "opencollective", diff --git a/package.js b/package.js index cb7f57ac0..d33aba3f2 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'faisalman:ua-parser-js', - version: '2.0.0-alpha.2', + version: '2.0.0-alpha.3', summary: 'Lightweight JavaScript-based user-agent string parser', git: 'https://github.com/faisalman/ua-parser-js.git', documentation: 'readme.md' diff --git a/package.json b/package.json index ea8379f8c..633425cdf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.3", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 6d88e813f..17a518a69 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-alpha.2 +/* Enums for UAParser.js v2.0.0-alpha.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index d766e247f..6479043bc 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-alpha.2 +/* Enums for UAParser.js v2.0.0-alpha.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 3ace7c1bb..aa3895bd4 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-alpha.2 +/* Extensions for UAParser.js v2.0.0-alpha.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index d0b6f8cdc..97e07f183 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-alpha.2 +/* Extensions for UAParser.js v2.0.0-alpha.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ @@ -28,109 +28,225 @@ const Apps = Object.freeze({ const Bots = Object.freeze({ browser : [ // Googlebot / BingBot / MSNBot / FacebookBot - [/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + [/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + + // GPTBot - https://platform.openai.com/docs/gptbot + [/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], // Slackbot - https://api.slack.com/robots [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] ] }); +const CLIs = Object.freeze({ + browser : [ + // wget / curl / lynx + [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] + ] +}); + const ExtraDevices = Object.freeze({ - device : [ - [ - /(nook)[\w ]+build\/(\w+)/i, // Nook - /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak - /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets - /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets - /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets - /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone - ], [VENDOR, MODEL, [TYPE, TABLET]], [ + device : [[ + /(nook)[\w ]+build\/(\w+)/i, // Nook + /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak + /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets + /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets + /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets + /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(u304aa)/i // AT&T - ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ + /(u304aa)/i // AT&T + ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ - /\bsie-(\w*)/i // Siemens - ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ + /\bsie-(\w*)/i // Siemens + ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ - /\b(rct\w+) b/i // RCA Tablets - ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ + /\b(rct\w+) b/i // RCA Tablets + ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ - /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets - ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ + /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets + ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ - /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet - ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ + /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet + ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ - /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet - ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ + /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet + ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ - /\b(tm\d{3}\w+) b/i - ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ + /\b(tm\d{3}\w+) b/i + ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ - /\b(k88) b/i // ZTE K Series Tablet - ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ + /\b(k88) b/i // ZTE K Series Tablet + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ - /\b(nx\d{3}j) b/i // ZTE Nubia - ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ + /\b(nx\d{3}j) b/i // ZTE Nubia + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ - /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile - ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ + /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ - /\b(zur\d{3}) b/i // Swiss ZUR Tablet - ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ + /\b(zur\d{3}) b/i // Swiss ZUR Tablet + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ - /\b((zeki)?tb.*\b) b/i // Zeki Tablets - ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ + /\b((zeki)?tb.*\b) b/i // Zeki Tablets + ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ - /\b([yr]\d{2}) b/i, - /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet - ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [ + /\b([yr]\d{2}) b/i, + /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet + ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [ - /\b(ns-?\w{0,9}) b/i // Insignia Tablets - ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ + /\b(ns-?\w{0,9}) b/i // Insignia Tablets + ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ - /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets - ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ + /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets + ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ - /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones - ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ + /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones + ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ - /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones - ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ + /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones + ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ - /\b(ph-1) /i // Essential PH-1 - ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ + /\b(ph-1) /i // Essential PH-1 + ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ - /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets - ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ + /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets + ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ - /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets - ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ + /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets + ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ - /\btu_(1491) b/i // Rotor Tablets - ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET] - ] + /\btu_(1491) b/i // Rotor Tablets + ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]] ] }); const Emails = Object.freeze({ browser : [ - // Microsoft Outlook / Thunderbird + // Microsoft Outlook / Thunderbird [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] ] }); -const CLI = Object.freeze({ +const MediaPlayers = Object.freeze({ + browser : [[ + + /(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia + /(coremedia) v([\w\._]+)/i + ], [NAME, VERSION], [ + + /(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer + ], [NAME, VERSION], [ + + /(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy + ], [NAME, VERSION], [ + + /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i, + // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC + // NSPlayer/PSP-InternetRadioPlayer/Videos + /(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD + /(lg player|nexplayer)\s([\d\.]+)/i, + /player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player + ], [NAME, VERSION], [ + /(nexplayer)\s([\w\.-]+)/i // Nexplayer + ], [NAME, VERSION], [ + + /(flrp)\/([\w\.-]+)/i // Flip Player + ], [[NAME, 'Flip Player'], VERSION], [ + + /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i + // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit + ], [NAME], [ + + /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i + // Gstreamer + ], [NAME, VERSION], [ + + /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player + /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i, + // Java/urllib/requests/wget/cURL + /(lavf)([\d\.]+)/i // Lavf (FFMPEG) + ], [NAME, VERSION], [ + + /(htc_one_s)\/([\d\.]+)/i, // HTC One S + ], [[NAME, /_/g, ' '], VERSION], [ + + /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i, + // MPlayer SVN + ], [NAME, VERSION], [ + + /(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer + /(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN + ], [NAME, VERSION], [ + + /(mplayer)/i, // MPlayer (no other info) + /(yourmuze)/i, // YourMuze + /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime + ], [NAME], [ + + /(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout + ], [NAME, VERSION], [ + + /(nokia\d+)\/([\w\.-]+)/i // Nokia + ], [NAME, VERSION], [ + + /\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird + ], [NAME, VERSION], [ + + /(winamp)3 version ([\w\.-]+)/i, // Winamp + /(winamp)\s([\w\.-]+)/i, + /(winamp)mpeg\/([\w\.-]+)/i + ], [NAME, VERSION], [ + + /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) + // inlight radio + ], [NAME], [ + + /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i + // QuickTime/RealMedia/RadioApp/RadioClientApplication/ + // SoundTap/Totem/Stagefright/Streamium + ], [NAME, VERSION], [ + + /(smp)([\d\.]+)/i // SMP + ], [NAME, VERSION], [ + + /(vlc) media player - version ([\w\.]+)/i, // VLC Videolan + /(vlc)\/([\w\.-]+)/i, + /(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp + /(foobar2000)\/([\d\.]+)/i, // Foobar2000 + /(itunes)\/([\d\.]+)/i // iTunes + ], [NAME, VERSION], [ + + /(wmplayer)\/([\w\.-]+)/i, // Windows Media Player + /(windows-media-player)\/([\w\.-]+)/i + ], [[NAME, /-/g, ' '], VERSION], [ + + /windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i, + // Windows Media Server + ], [VERSION, [NAME, 'Windows']], [ + + /(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm + ], [NAME, VERSION], [ + + /(rad.io)\s([\d\.]+)/i, // Rad.io + /(radio.(?:de|at|fr))\s([\d\.]+)/i + ], [[NAME, 'rad.io'], VERSION] + ] +}); + +const Modules = Object.freeze({ browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] + // Axios/jsdom/Scrapy + [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']] ] }); export { Apps, Bots, + CLIs, ExtraDevices, Emails, - CLI + MediaPlayers, + Modules }; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 9e6c3395a..85e830b73 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-alpha.2 +/* Helpers for UAParser.js v2.0.0-alpha.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs new file mode 100644 index 000000000..37ec40122 --- /dev/null +++ b/src/helpers/ua-parser-helpers.mjs @@ -0,0 +1,46 @@ +// Generated ESM version of UAParser.js helpers +// DO NOT EDIT THIS FILE! +// Source: /src/helpers/ua-parser-helpers.js + +/////////////////////////////////////////////// +/* Helpers for UAParser.js v2.0.0-alpha.3 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +/* + # Reference: + https://www.chromium.org/updates/ua-reduction/ + + # Desktop + --- + Format: + Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 + + Possible values: + - Windows NT 10.0; Win64; x64 + - Macintosh; Intel Mac OS X 10_15_7 + - X11; Linux x86_64 + - X11; CrOS x86_64 14541.0.0 + - Fuchsia + + # Mobile & Tablet: (except iOS/Android WebView) + --- + Format: + Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 + + Possible values: + - "Mobile" + - "" (empty string for Tablets & Desktop) +*/ + +const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/; + +const isFrozenUA = str => frozenUA.test(str); + +export { + isFrozenUA +}; \ No newline at end of file diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 861bb105b..793b248c1 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-alpha.2 +/* UAParser.js v2.0.0-alpha.3 Copyright © 2012-2023 Faisal Salman MIT License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -20,7 +20,7 @@ ///////////// - var LIBVERSION = '2.0.0-alpha.2', + var LIBVERSION = '2.0.0-alpha.3', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -50,12 +50,12 @@ CH_HEADER = 'sec-ch-ua', CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', - CH_HEADER_BITNESS = CH_HEADER + '-bitness', - CH_HEADER_MOBILE = CH_HEADER + '-mobile', - CH_HEADER_MODEL = CH_HEADER + '-model', - CH_HEADER_PLATFORM = CH_HEADER + '-platform', + CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, + CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, + CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, + CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'], + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index e49eb7278..6f716d620 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-alpha.2 +/* UAParser.js v2.0.0-alpha.3 Copyright © 2012-2023 Faisal Salman MIT License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -22,7 +22,7 @@ ///////////// - var LIBVERSION = '2.0.0-alpha.2', + var LIBVERSION = '2.0.0-alpha.3', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -52,12 +52,12 @@ CH_HEADER = 'sec-ch-ua', CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', - CH_HEADER_BITNESS = CH_HEADER + '-bitness', - CH_HEADER_MOBILE = CH_HEADER + '-mobile', - CH_HEADER_MODEL = CH_HEADER + '-model', - CH_HEADER_PLATFORM = CH_HEADER + '-platform', + CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, + CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, + CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, + CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'], + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', @@ -349,7 +349,7 @@ /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android - /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram + /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA']], [ @@ -476,7 +476,7 @@ ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ // Xiaomi - /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO + /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi @@ -584,7 +584,7 @@ ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [ // MIXED - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i, + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron /(hp) ([\w ]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i, // Asus @@ -629,7 +629,7 @@ ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ /crkey/i // Google Chromecast ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ - /droid.+aft(\w)( bui|\))/i // Fire TV + /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ /\(dtv[\);].+(aquos)/i, /(aquos-tv[\w ]+)\)/i // Sharp @@ -927,7 +927,6 @@ setProps.call(this, [ [BRANDS, itemListToArray(uach[CH_HEADER])], [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], - [BRANDS, itemListToArray(uach[CH_HEADER])], [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], [MODEL, stripQuotes(uach[CH_HEADER_MODEL])], [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], @@ -1149,7 +1148,8 @@ ['getResult', createItemFunc(UA_RESULT)], ['getUA', function () { return userAgent; }], ['setUA', function (ua) { - userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua; + if (typeof ua === STR_TYPE) + userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua; return this; }] ]) From 2046b77ede0f8cb39971e948792af84d272fd5dc Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 20 Aug 2023 13:41:03 +0700 Subject: [PATCH 113/388] Improve `device.type` detection using client hints "form-factor" data --- src/main/ua-parser.js | 15 +++++++++++++++ test/mocha-test.js | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 793b248c1..7047e8327 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -43,6 +43,7 @@ USER_AGENT = 'user-agent', UA_MAX_LENGTH = 350, BRANDS = 'brands', + FORMFACTOR = 'formFactor', FULLVERLIST = 'fullVersionList', PLATFORM = 'platform', PLATFORMVER = 'platformVersion', @@ -51,6 +52,7 @@ CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, + CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor', CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, @@ -256,6 +258,15 @@ '8.1' : 'NT 6.3', '10' : ['NT 6.4', 'NT 10.0'], 'RT' : 'ARM' + }, + + formFactorMap = { + 'embedded' : 'Automotive', + 'mobile' : 'Mobile', + 'tablet' : 'Tablet', + 'smarttv' : 'TV', + 'wearable' : ['VR', 'XR'], + '?' : 'Unknown' }; ////////////// @@ -930,6 +941,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], + [FORMFACTOR, stripQuotes(uach[CH_HEADER_FORM_FACTOR])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1044,6 +1056,9 @@ if (uaCH[MODEL]) { this.set(MODEL, uaCH[MODEL]); } + if (uaCH[FORMFACTOR]) { + this.set(TYPE, strMapper(uaCH[FORMFACTOR], formFactorMap)); + } break; case UA_OS: var osName = uaCH[PLATFORM]; diff --git a/test/mocha-test.js b/test/mocha-test.js index 682f710e0..e1db526ab 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -462,4 +462,23 @@ describe('Map UA-CH headers', function () { assert.strictEqual(ua.device.is("tablet"), false); }); }); + + it('Can detect form-factor from client-hints', function () { + + const FFVR = { + 'sec-ch-ua-form-factor' : 'VR' + }; + + const FFUnknown = { + 'sec-ch-ua-form-factor' : 'Unknown' + }; + + UAParser(FFVR).withClientHints().then(function (ua) { + assert.strictEqual(ua.device.type, 'wearable'); + }); + + UAParser(FFUnknown).withClientHints().then(function (ua) { + assert.strictEqual(ua.device.type, undefined); + }); + }); }); \ No newline at end of file From 594806072930fa88966e02640f81b775fe57e71d Mon Sep 17 00:00:00 2001 From: Andreas Kogler Date: Sun, 20 Aug 2023 09:25:56 +0200 Subject: [PATCH 114/388] Implement PlayStation app `WebMAF` detection (#649) * Add support for `WebMAF` detection on Playstation * Extend `WebMAF` support for PlayStation 5 * Update changelog --- changelog.md | 1 + src/main/ua-parser.js | 1 + test/specs/browser-all.json | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/changelog.md b/changelog.md index ac8e493be..3baed6c59 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ - What's breaking: - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` + - Apps on PlayStation 4/5 now report `WebMAF` instead of `WebKit` for `browser.name` and the `WebMAF`-version in `browser.version` - What's new: - Add some new methods in result object: - Add support for client hints: `withClientHints()` diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a8ed359e7..c2b736d4a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -285,6 +285,7 @@ // Mixed /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer + /(webmaf)\/([a-z0-9\.-]+)/i, // Sony/Playstation WebMAF // Trident based /(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 9ca190c61..d876d5508 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1148,6 +1148,26 @@ "major" : "undefined" } }, + { + "desc" : "Sony WebMAF SDK (Playstation)", + "ua" : "Mozilla/5.0 (PlayStation 4 WebMAF) AppleWebKit/601.2 (KHTML, like Gecko) WebMAF/v3.1.0-0-ge5873ba4 SDK: (0x09508001u), Built: Nov 1 2022 14:36:14", + "expect" : + { + "name" : "WebMAF", + "version" : "v3.1.0-0-ge5873ba4", + "major" : "3" + } + }, + { + "desc" : "Sony WebMAF SDK (Playstation)", + "ua" : "Mozilla/5.0 (PlayStation 5; WebMAF/1.0.0) AppleWebKit/537.73 (KHTML, like Gecko)", + "expect" : + { + "name" : "WebMAF", + "version" : "1.0.0", + "major" : "1" + } + }, { "desc" : "Swiftfox", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0 (Swiftfox)", From 73a936001a96e8d1f6be847ec169b6574f7664c4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 21 Aug 2023 11:28:30 +0700 Subject: [PATCH 115/388] Modify `ua-parser-js/helpers` submodule into `@ua-parser-js/helpers` scoped package --- package-lock.json | 12 ++++++++++++ package.json | 7 +++---- src/helpers/package.json | 29 +++++++++++++++++++++++++++++ src/helpers/ua-parser-helpers.js | 6 ++---- src/helpers/ua-parser-helpers.mjs | 6 ++---- 5 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 src/helpers/package.json diff --git a/package-lock.json b/package-lock.json index f0d514f4f..9d1ca35ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,9 @@ } ], "license": "MIT", + "workspaces": [ + "src/helpers" + ], "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", @@ -760,6 +763,10 @@ "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", "dev": true }, + "node_modules/@ua-parser-js/helpers": { + "resolved": "src/helpers", + "link": true + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -3770,6 +3777,11 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "src/helpers": { + "name": "@ua-parser-js/helpers", + "version": "2.0.0-alpha.3", + "license": "MIT" } } } diff --git a/package.json b/package.json index 633425cdf..205980ee7 100644 --- a/package.json +++ b/package.json @@ -157,10 +157,6 @@ "./extensions": { "require": "./src/extensions/ua-parser-extensions.js", "import": "./src/extensions/ua-parser-extensions.mjs" - }, - "./helpers": { - "require": "./src/helpers/ua-parser-helpers.js", - "import": "./src/helpers/ua-parser-helpers.mjs" } }, "files": [ @@ -217,5 +213,8 @@ "type": "github", "url": "https://github.com/sponsors/faisalman" } + ], + "workspaces": [ + "src/helpers" ] } diff --git a/src/helpers/package.json b/src/helpers/package.json new file mode 100644 index 000000000..fe99fb285 --- /dev/null +++ b/src/helpers/package.json @@ -0,0 +1,29 @@ +{ + "title": "UAParser.js Helpers", + "name": "@ua-parser-js/helpers", + "version": "2.0.0-alpha.3", + "author": "Faisal Salman ", + "description": "Helpers for UAParser.js", + "main": "ua-parser-helpers.js", + "module": "ua-parser-helpers.mjs", + "scripts" : { + "test": "echo 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/faisalman/ua-parser-js.git" + }, + "keywords": [ + "ua-parser-js", + "browser-detection", + "device-detection", + "os-detection", + "user-agent", + "client-hints" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/faisalman/ua-parser-js/issues" + }, + "homepage": "https://github.com/faisalman/ua-parser-js#readme" +} \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 85e830b73..eaa317143 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-alpha.3 +/* Helpers for UAParser.js https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ @@ -33,9 +33,7 @@ - "" (empty string for Tablets & Desktop) */ -const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/; - -const isFrozenUA = str => frozenUA.test(str); +const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); module.exports = { isFrozenUA diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index 37ec40122..bf0dd6650 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-alpha.3 +/* Helpers for UAParser.js https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ @@ -37,9 +37,7 @@ - "" (empty string for Tablets & Desktop) */ -const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/; - -const isFrozenUA = str => frozenUA.test(str); +const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); export { isFrozenUA From 3dd4b60ee97ad1e9485a5dbeef1c9f136e3fb9a3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 22 Aug 2023 01:09:35 +0700 Subject: [PATCH 116/388] [helpers] Add new method: `unfreezeUA()`, construct new unfreezed user-agent string using real data from client hints --- src/helpers/package.json | 4 +- src/helpers/readme.md | 65 +++++++++++++++++++++++++++ src/helpers/ua-parser-helpers.d.ts | 2 + src/helpers/ua-parser-helpers.js | 63 ++++++++++++++++++++++++-- test/mocha-test-helpers.js | 2 +- test/playwright-test-helpers.spec.mjs | 47 +++++++++++++++++++ 6 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 src/helpers/readme.md create mode 100644 src/helpers/ua-parser-helpers.d.ts create mode 100644 test/playwright-test-helpers.spec.mjs diff --git a/src/helpers/package.json b/src/helpers/package.json index fe99fb285..092ffc956 100644 --- a/src/helpers/package.json +++ b/src/helpers/package.json @@ -1,9 +1,9 @@ { "title": "UAParser.js Helpers", "name": "@ua-parser-js/helpers", - "version": "2.0.0-alpha.3", + "version": "0.0.1", "author": "Faisal Salman ", - "description": "Helpers for UAParser.js", + "description": "A collection of utility methods for UAParser.js", "main": "ua-parser-helpers.js", "module": "ua-parser-helpers.mjs", "scripts" : { diff --git a/src/helpers/readme.md b/src/helpers/readme.md new file mode 100644 index 000000000..e23abc2e7 --- /dev/null +++ b/src/helpers/readme.md @@ -0,0 +1,65 @@ +# @ua-parser-js/helpers + +This package contains a collection of utility methods for [UAParser.js](https://github.com/faisalman/ua-parser-js) + +```sh +npm i @ua-parser-js/helpers +``` + +### * `isFrozenUA(ua:string):boolean` + +Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/) + +### * `unfreezeUA():Promise` + +construct new unfreezed user-agent string using real data from client hints + +## Code Example + +```js +import { isFrozenUA } from '@ua-parser-js/helpers'; + +const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36"; +const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36"; + +console.log(isFrozenUA(regularMobileUA)); +// false + +console.log(isFrozenUA(freezedMobileUA)); +// true +``` + +```js +import { unfreezeUA } from '@ua-parser-js/helpers'; + +/* + Suppose we're in a browser having this client hints data: + + { + fullVersionList: [ + { + brand: 'New Browser', + version: '110.1.2.3' + }, + { + brand: 'Chromium', + version: '110.1.2.3' + }, + { + brand: 'Not(A:Brand', + version: '110' + } + ], + platform: 'Windows', + platformVersion: '13.0.0' + } + + And a freezed user-agent: + + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36' +*/ + +unfreezeUA(); + +// 'Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36' +``` \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts new file mode 100644 index 000000000..61e041f48 --- /dev/null +++ b/src/helpers/ua-parser-helpers.d.ts @@ -0,0 +1,2 @@ +export function isFrozenUA(ua: string): boolean; +export function unfreezeUA(): Promise; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index eaa317143..45933eb90 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,11 +1,11 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js +/* A collection of utility methods for UAParser.js https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ ////////////////////////////////////////////// -/*jshint esversion: 6 */ +/*jshint esversion: 11 */ /* # Reference: @@ -33,8 +33,63 @@ - "" (empty string for Tablets & Desktop) */ -const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); +const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua); + +const unfreezeUA = async () => { + if (!navigator) { + throw new Error('Currently only support browser environment'); + } else { + let ua = navigator.userAgent; + if (navigator.userAgentData && isFrozenUA(ua)) { + const ch = await navigator.userAgentData.getHighEntropyValues(['architecture', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']); + switch (ch.platform) { + case 'Windows': + let [major, minor] = ch.platformVersion?.split('.').map(i => parseInt(i, 10)); + let osReplacer = (major < 1) ? + `$ 6.${minor}` : + (major >= 13) ? + `$ 11.${minor}` : + `$ 10.${minor}`; + let archReplacer = (ch.architecture == 'arm') ? + '; ARM' : + (ch.wow64) ? + '; WOW64' : + (ch.architecture == 'x86' && ch.bitness == '64') ? + '; $' : ''; + ua = ua.replace(/(?Windows NT) 10\.0/, osReplacer) + .replace(/; (?Win64; x64)/, archReplacer); + break; + case 'Android': + ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`); + break; + case 'Linux': + case 'Chrome OS': + archReplacer = (ch.architecture == 'arm') ? + ((ch.bitness == '64') ? 'arm64' : 'arm') : + (ch.architecture == 'x86' && ch.bitness == '64') ? + '$' : 'x86'; + + ua = ua.replace(/(?x86_64)/, archReplacer); + break; + case 'macOS': + ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`); + break; + } + let browserReplacer = ''; + ch.fullVersionList?.forEach((browser) => { + if (!/not.a.brand/i.test(browser.brand)) { + browserReplacer += `${browser.brand}/${browser.version} `; + } + }); + if (browserReplacer) { + ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /, browserReplacer); + } + } + return ua; + } +}; module.exports = { - isFrozenUA + isFrozenUA, + unfreezeUA }; \ No newline at end of file diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index b954e73cc..c8417d629 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,4 +1,4 @@ -const { isFrozenUA } = require('ua-parser-js/helpers'); +const { isFrozenUA } = require('@ua-parser-js/helpers'); const assert = require('assert'); describe('isFrozenUA', () => { diff --git a/test/playwright-test-helpers.spec.mjs b/test/playwright-test-helpers.spec.mjs new file mode 100644 index 000000000..c5bc65e70 --- /dev/null +++ b/test/playwright-test-helpers.spec.mjs @@ -0,0 +1,47 @@ +// @ts-check +import { test, expect } from '@playwright/test'; +import { unfreezeUA } from '@ua-parser-js/helpers'; + +test('test for unfreezeUA() method', async ({ page }) => { + + + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgentData', { + value: { + brands: [], + platform: 'Windows', + mobile: false, + getHighEntropyValues: () => { + return Promise.resolve({ + architecture: 'x86', + bitness: '64', + fullVersionList: [ + { + brand: 'New Browser', + version: '110.1.2.3' + }, + { + brand: 'Chromium', + version: '110.1.2.3' + }, + { + brand: 'Not(A:Brand', + version: '110' + } + ], + platform: 'Windows', + platformVersion: '0.3' + }); + } + } + }); + Object.defineProperty(navigator, 'userAgent', { + value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36' + }); + }); + await page.goto('about:blank'); + await page.addScriptTag({ path: './src/helpers/ua-parser-helpers.js' }); + // @ts-ignore + const ua = await page.evaluate(async () => await unfreezeUA()); + expect(ua).toBe('Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'); +}); \ No newline at end of file From 129657673bec3e59a5e0be34d6ba15e47cc5e716 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 22 Aug 2023 23:39:34 +0700 Subject: [PATCH 117/388] [helpers] Add new method: `UACHParser()`, parse client-hints HTTP headers into its JS API equivalent --- src/helpers/package.json | 6 +- src/helpers/readme.md | 72 +++++++++++- src/helpers/ua-parser-helpers.d.ts | 37 +++++- src/helpers/ua-parser-helpers.js | 177 +++++++++++++++++++++-------- test/mocha-test-helpers.js | 64 ++++++++++- 5 files changed, 297 insertions(+), 59 deletions(-) diff --git a/src/helpers/package.json b/src/helpers/package.json index 092ffc956..b5167cfaa 100644 --- a/src/helpers/package.json +++ b/src/helpers/package.json @@ -1,12 +1,12 @@ { "title": "UAParser.js Helpers", "name": "@ua-parser-js/helpers", - "version": "0.0.1", + "version": "0.0.3", "author": "Faisal Salman ", "description": "A collection of utility methods for UAParser.js", "main": "ua-parser-helpers.js", "module": "ua-parser-helpers.mjs", - "scripts" : { + "scripts": { "test": "echo 1" }, "repository": { @@ -26,4 +26,4 @@ "url": "https://github.com/faisalman/ua-parser-js/issues" }, "homepage": "https://github.com/faisalman/ua-parser-js#readme" -} \ No newline at end of file +} diff --git a/src/helpers/readme.md b/src/helpers/readme.md index e23abc2e7..5f1efd2c8 100644 --- a/src/helpers/readme.md +++ b/src/helpers/readme.md @@ -12,7 +12,11 @@ Check whether a user-agent string match with [frozen user-agent pattern](https:/ ### * `unfreezeUA():Promise` -construct new unfreezed user-agent string using real data from client hints +Construct new unfreezed user-agent string using real data from client hints + +### * `UACHParser(headers: object): object` + +Parse client hints HTTP headers (sec-ch-ua) into its JS API equivalent ## Code Example @@ -59,7 +63,69 @@ import { unfreezeUA } from '@ua-parser-js/helpers'; 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36' */ -unfreezeUA(); - +unfreezeUA() + .then(ua => console.log(ua)); // 'Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36' +``` + +```js +import { UACHParser } from '@ua-parser-js/helpers'; + +/* + Suppose we're in a server having this client hints data: + + const headers = { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : 'arm', + 'sec-ch-ua-bitness' : '64', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : 'Pixel 99', + 'sec-ch-ua-platform' : 'Linux', + 'sec-ch-ua-platform-version' : '13', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; +*/ + +const userAgentData = UACHParser(headers); + +console.log(userAgentData); +/* + { + "architecture": "arm", + "bitness": "64", + "brands": [ + { + "brand": "Chromium", + "version": "93" + }, + { + "brand": "Google Chrome", + "version": "93" + }, + { + "brand": " Not;A Brand", + "version": "99" + } + ], + "fullVersionList": [ + { + "brand": "Chromium", + "version": "93.0.1.2" + }, + { + "brand": "Google Chrome", + "version": "93.0.1.2" + }, + { + "brand": " Not;A Brand", + "version": "99.0.1.2" + } + ], + "mobile": true, + "model": "Pixel 99", + "platform": "Linux", + "platformVersion": "13" + } +*/ ``` \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 61e041f48..836de09d4 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,2 +1,37 @@ +interface IBrowser { + brand: string; + version: string; +} + +interface ClientHintsJSLowEntropy { + brands: Array; + mobile: boolean; + platform: string; +} + +export interface ClientHintsJSHighEntropy extends ClientHintsJSLowEntropy { + architecture?: string; + bitness?: string; + formFactor?: string; + fullVersionList?: Array; + model?: string; + platformVersion?: string; + wow64?: boolean; +}; + +export interface ClientHintsHTTPHeaders { + 'sec-ch-ua-arch'?: string; + 'sec-ch-ua-bitness'?: string; + 'sec-ch-ua'?: string; + 'sec-ch-ua-form-factor'?: string; + 'sec-ch-ua-full-version-list'?: string; + 'sec-ch-ua-mobile'?: string; + 'sec-ch-ua-model'?: string; + 'sec-ch-ua-platform'?: string; + 'sec-ch-ua-platform-version'?: string; + 'sec-ch-ua-wow64'?: string; +} + export function isFrozenUA(ua: string): boolean; -export function unfreezeUA(): Promise; \ No newline at end of file +export function unfreezeUA(): Promise; +export function UACHParser(headers: ClientHintsHTTPHeaders): ClientHintsJSHighEntropy; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 45933eb90..26d28f726 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -35,61 +35,138 @@ const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua); -const unfreezeUA = async () => { - if (!navigator) { - throw new Error('Currently only support browser environment'); +const unfreezeUA = async (ua, ch) => { + const env = typeof navigator == 'undefined' ? 'node' : 'browser'; + if (env == 'node') { + if (!ua['user-agent']) { + throw new Error('User-Agent header not found'); + } + ch = UACHParser(ua); + ua = ua['user-agent']; } else { - let ua = navigator.userAgent; - if (navigator.userAgentData && isFrozenUA(ua)) { - const ch = await navigator.userAgentData.getHighEntropyValues(['architecture', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']); - switch (ch.platform) { - case 'Windows': - let [major, minor] = ch.platformVersion?.split('.').map(i => parseInt(i, 10)); - let osReplacer = (major < 1) ? - `$ 6.${minor}` : - (major >= 13) ? - `$ 11.${minor}` : - `$ 10.${minor}`; - let archReplacer = (ch.architecture == 'arm') ? - '; ARM' : - (ch.wow64) ? - '; WOW64' : - (ch.architecture == 'x86' && ch.bitness == '64') ? - '; $' : ''; - ua = ua.replace(/(?Windows NT) 10\.0/, osReplacer) - .replace(/; (?Win64; x64)/, archReplacer); - break; - case 'Android': - ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`); - break; - case 'Linux': - case 'Chrome OS': - archReplacer = (ch.architecture == 'arm') ? - ((ch.bitness == '64') ? 'arm64' : 'arm') : - (ch.architecture == 'x86' && ch.bitness == '64') ? - '$' : 'x86'; - - ua = ua.replace(/(?x86_64)/, archReplacer); - break; - case 'macOS': - ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`); - break; - } - let browserReplacer = ''; - ch.fullVersionList?.forEach((browser) => { - if (!/not.a.brand/i.test(browser.brand)) { - browserReplacer += `${browser.brand}/${browser.version} `; - } - }); - if (browserReplacer) { - ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /, browserReplacer); - } + ua = ua || navigator.userAgent; + ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']); + } + if (isFrozenUA(ua) && ch) { + switch (ch.platform) { + case 'Windows': + let [major, minor] = ch.platformVersion + .split('.') + .map(num => parseInt(num, 10)); + major = (major < 1) ? '6' : (major >= 13) ? '11' : '10'; + ua = ua .replace(/(?Windows NT) 10\.0/, `$ ${major}.${minor}`) + .replace(/; (?Win64; x64)/, + (ch.architecture == 'arm') ? + '; ARM' : + (ch.wow64) ? + '; WOW64' : + (ch.architecture == 'x86' && ch.bitness != '64') ? + '' : '; $'); + break; + case 'Android': + ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`); + break; + case 'Linux': + case 'Chrome OS': + ua = ua.replace(/(?x86_64)/, + (ch.architecture == 'arm') ? + ((ch.bitness == '64') ? 'arm64' : 'arm') : + (ch.architecture == 'x86' && ch.bitness != '64') ? + 'x86' : '$'); + break; + case 'macOS': + ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`); + break; + } + if (ch.fullVersionList) { + ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /, + ch.fullVersionList + .filter(browser => !/not.a.brand/i.test(browser.brand)) + .map(browser => `${browser.brand.replace(/^google /i,'')}/${browser.version} `) + .join('')); } - return ua; } + return ua; +}; + +const UACHMap = { + 'sec-ch-ua-arch' : { + prop : 'architecture', + type : 'sf-string' + }, + 'sec-ch-ua-bitness' : { + prop : 'bitness', + type : 'sf-string' + }, + 'sec-ch-ua' : { + prop : 'brands', + type : 'sf-list' + }, + 'sec-ch-ua-form-factor' : { + prop : 'formFactor', + type : 'sf-string' + }, + 'sec-ch-ua-full-version-list' : { + prop : 'fullVersionList', + type : 'sf-list' + }, + 'sec-ch-ua-mobile' : { + prop : 'mobile', + type : 'sf-boolean', + }, + 'sec-ch-ua-model' : { + prop : 'model', + type : 'sf-string', + }, + 'sec-ch-ua-platform' : { + prop : 'platform', + type : 'sf-string' + }, + 'sec-ch-ua-platform-version' : { + prop : 'platformVersion', + type : 'sf-string' + }, + 'sec-ch-ua-wow64' : { + prop : 'wow64', + type : 'sf-boolean' + } +}; + +const UACHParser = (headers) => { + const parse = (str, type) => { + if (!str) { + return ''; + } + switch (type) { + case 'sf-boolean': + return /\?1/.test(str); + case 'sf-list': + return str.replace(/\\?\"/g, '') + .split(', ') + .map(brands => { + const [brand, version] = brands.split(';v='); + return { + brand : brand, + version : version + }; + }); + case 'sf-string': + default: + return str.replace(/\\?\"/g, ''); + } + }; + let ch = {}; + Object.keys(UACHMap).forEach(field => { + if (headers.hasOwnProperty(field)) { + const { prop, type } = UACHMap[field]; + ch[prop] = parse(headers[field], type); + } + }); + return ch; }; module.exports = { isFrozenUA, - unfreezeUA + unfreezeUA, + UACHParser }; \ No newline at end of file diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index c8417d629..07ba15c6c 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,7 +1,7 @@ -const { isFrozenUA } = require('@ua-parser-js/helpers'); +const { isFrozenUA, unfreezeUA, UACHParser } = require('@ua-parser-js/helpers'); const assert = require('assert'); -describe('isFrozenUA', () => { +describe('isFrozenUA()', () => { it('Returns whether a user agent is frozen', () => { const regularWindowsUA = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36"; @@ -32,4 +32,64 @@ describe('isFrozenUA', () => { assert.strictEqual(isFrozenUA(regularTabletUA), false); assert.strictEqual(isFrozenUA(freezedTabletUA), true); }); +}); + +const headers = { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : 'arm', + 'sec-ch-ua-bitness' : '64', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : 'Pixel 99', + 'sec-ch-ua-platform' : 'Linux', + 'sec-ch-ua-platform-version' : '13', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' +}; + +describe('unfreezeUA()', () => { + it('returns an unfreezed user-agent using real data from client hints HTTP headers (sec-ch-ua)', async () => { + const unfreezed = await unfreezeUA(headers); + assert.strictEqual(unfreezed, 'Mozilla/5.0 (X11; Linux arm64) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/93.0.1.2 Chrome/93.0.1.2 Safari/537.36'); + }); +}); + +describe('UACHParser()', () => { + it('parse client hints HTTP headers (sec-ch-ua) into a JavaScript object', () => { + assert.deepEqual(UACHParser(headers), { + "architecture": "arm", + "bitness": "64", + "brands": [ + { + "brand": "Chromium", + "version": "93" + }, + { + "brand": "Google Chrome", + "version": "93" + }, + { + "brand": " Not;A Brand", + "version": "99" + } + ], + "fullVersionList": [ + { + "brand": "Chromium", + "version": "93.0.1.2" + }, + { + "brand": "Google Chrome", + "version": "93.0.1.2" + }, + { + "brand": " Not;A Brand", + "version": "99.0.1.2" + } + ], + "mobile": true, + "model": "Pixel 99", + "platform": "Linux", + "platformVersion": "13" + }); + }); }); \ No newline at end of file From 3f105fe93b8761a7d048f1d3f812c4f94bcf3c2e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 23 Aug 2023 14:38:34 +0700 Subject: [PATCH 118/388] [helpers] split `helpers` into 2 new packages: `user-agent-helpers` & `client-hints-helpers` --- package-lock.json | 24 +++- script/build-module.js | 89 ++++++------ .../client-hints-helpers.d.ts} | 2 - .../client-hints-helpers.js | 88 ++++++++++++ .../client-hints-helpers.mjs | 92 ++++++++++++ .../package.json | 14 +- src/client-hints-helpers/readme.md | 75 ++++++++++ src/helpers/readme.md | 131 ------------------ src/helpers/ua-parser-helpers.mjs | 44 ------ src/user-agent-helpers/package.json | 32 +++++ src/user-agent-helpers/readme.md | 67 +++++++++ .../user-agent-helpers.d.ts | 4 + .../user-agent-helpers.js} | 87 +----------- src/user-agent-helpers/user-agent-helpers.mjs | 101 ++++++++++++++ test/mocha-test-helpers.js | 5 +- test/playwright-test-helpers.spec.mjs | 47 ------- 16 files changed, 543 insertions(+), 359 deletions(-) rename src/{helpers/ua-parser-helpers.d.ts => client-hints-helpers/client-hints-helpers.d.ts} (90%) create mode 100644 src/client-hints-helpers/client-hints-helpers.js create mode 100644 src/client-hints-helpers/client-hints-helpers.mjs rename src/{helpers => client-hints-helpers}/package.json (62%) create mode 100644 src/client-hints-helpers/readme.md delete mode 100644 src/helpers/readme.md delete mode 100644 src/helpers/ua-parser-helpers.mjs create mode 100644 src/user-agent-helpers/package.json create mode 100644 src/user-agent-helpers/readme.md create mode 100644 src/user-agent-helpers/user-agent-helpers.d.ts rename src/{helpers/ua-parser-helpers.js => user-agent-helpers/user-agent-helpers.js} (63%) create mode 100644 src/user-agent-helpers/user-agent-helpers.mjs delete mode 100644 test/playwright-test-helpers.spec.mjs diff --git a/package-lock.json b/package-lock.json index 9d1ca35ff..613ed7399 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,8 @@ ], "license": "MIT", "workspaces": [ - "src/helpers" + "src/client-hints-helpers", + "src/user-agent-helpers" ], "devDependencies": { "@babel/parser": "7.15.8", @@ -763,8 +764,12 @@ "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", "dev": true }, - "node_modules/@ua-parser-js/helpers": { - "resolved": "src/helpers", + "node_modules/@ua-parser-js/client-hints-helpers": { + "resolved": "src/client-hints-helpers", + "link": true + }, + "node_modules/@ua-parser-js/user-agent-helpers": { + "resolved": "src/user-agent-helpers", "link": true }, "node_modules/@ungap/promise-all-settled": { @@ -3778,9 +3783,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "src/client-hints-helpers": { + "name": "@ua-parser-js/client-hints-helpers", + "version": "0.0.1", + "license": "MIT" + }, "src/helpers": { "name": "@ua-parser-js/helpers", - "version": "2.0.0-alpha.3", + "version": "0.0.3", + "extraneous": true, + "license": "MIT" + }, + "src/user-agent-helpers": { + "name": "@ua-parser-js/user-agent-helpers", + "version": "0.0.1", "license": "MIT" } } diff --git a/script/build-module.js b/script/build-module.js index d7f5a59ae..3b00317f6 100755 --- a/script/build-module.js +++ b/script/build-module.js @@ -1,57 +1,64 @@ #!/usr/bin/env node /* jshint esversion: 6 */ const fs = require('fs'); -const PATH = { - main : { - src : 'src/main/ua-parser.js', - dest : 'src/main/ua-parser.mjs', - title : '' - }, - enums : { - src : 'src/enums/ua-parser-enums.js', - dest :'src/enums/ua-parser-enums.mjs', - title : 'enums' - }, - extensions : { - src : 'src/extensions/ua-parser-extensions.js', - dest : 'src/extensions/ua-parser-extensions.mjs', - title : 'extensions' - }, - helpers : { - src : 'src/helpers/ua-parser-helpers.js', - dest : 'src/helpers/ua-parser-helpers.mjs', - title : 'helpers' - } -}; -const generateMJS = (module, replacers) => { - const { src, dest, title } = PATH[module]; + +const generateMJS = (module) => { + let { src, dest, title, replacements } = module; let text = fs.readFileSync(src, 'utf-8'); - replacers.forEach(replacer => { - text = text.replace(replacer[0], replacer[1]); + + replacements.push( + [/const (.+?)\s*=\s*require\((.+)\)/ig, 'import $1 from $2'], + [/module\.exports =/ig, 'export'] + ); + replacements.forEach(rep => { + text = text.replace(rep[0], rep[1]); }); console.log(`Generate ${dest}`); fs.writeFileSync(dest, -`// Generated ESM version of UAParser.js ${title} +`// Generated ESM version of ${title} // DO NOT EDIT THIS FILE! // Source: /${src} ${text}`, 'utf-8'); -}; - -// ua-parser.mjs -generateMJS('main', [ - [/\(func[\s\S]+strict\';/ig, ''], - [/esversion\: 3/ig, 'esversion: 6'], - [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'] -]); -// ua-parser-enum.mjs -generateMJS('enums', [[/module\.exports =/ig, 'export']]); +}; -// ua-parser-extension.mjs -generateMJS('extensions', [[/module\.exports =/ig, 'export']]); +const modules = [ + { + src : 'src/main/ua-parser.js', + dest : 'src/main/ua-parser.mjs', + title : 'ua-parser-js', + replacements : [ + [/\(func[\s\S]+strict\';/ig, ''], + [/esversion\: 3/ig, 'esversion: 6'], + [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'] + ] + },{ + src : 'src/enums/ua-parser-enums.js', + dest :'src/enums/ua-parser-enums.mjs', + title : 'ua-parser-js/enums', + replacements : [] + }, + { + src : 'src/extensions/ua-parser-extensions.js', + dest : 'src/extensions/ua-parser-extensions.mjs', + title : 'ua-parser-js/extensions', + replacements : [] + }, + { + src : 'src/user-agent-helpers/user-agent-helpers.js', + dest : 'src/user-agent-helpers/user-agent-helpers.mjs', + title : '@ua-parser-js/user-agent-helpers', + replacements : [] + }, + { + src : 'src/client-hints-helpers/client-hints-helpers.js', + dest : 'src/client-hints-helpers/client-hints-helpers.mjs', + title : '@ua-parser-js/client-hints-helpers', + replacements : [] + } +]; -// ua-parser-helpers.mjs -generateMJS('helpers', [[/module\.exports =/ig, 'export']]); \ No newline at end of file +modules.forEach(module => generateMJS(module)); \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/client-hints-helpers/client-hints-helpers.d.ts similarity index 90% rename from src/helpers/ua-parser-helpers.d.ts rename to src/client-hints-helpers/client-hints-helpers.d.ts index 836de09d4..00f98f84b 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/client-hints-helpers/client-hints-helpers.d.ts @@ -32,6 +32,4 @@ export interface ClientHintsHTTPHeaders { 'sec-ch-ua-wow64'?: string; } -export function isFrozenUA(ua: string): boolean; -export function unfreezeUA(): Promise; export function UACHParser(headers: ClientHintsHTTPHeaders): ClientHintsJSHighEntropy; \ No newline at end of file diff --git a/src/client-hints-helpers/client-hints-helpers.js b/src/client-hints-helpers/client-hints-helpers.js new file mode 100644 index 000000000..5fa3bbc77 --- /dev/null +++ b/src/client-hints-helpers/client-hints-helpers.js @@ -0,0 +1,88 @@ +//////////////////////////////////////////////////// +/* A collection of utility methods for client-hints + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +/////////////////////////////////////////////////// + +/*jshint esversion: 11 */ + +const UACHMap = { + 'sec-ch-ua-arch' : { + prop : 'architecture', + type : 'sf-string' + }, + 'sec-ch-ua-bitness' : { + prop : 'bitness', + type : 'sf-string' + }, + 'sec-ch-ua' : { + prop : 'brands', + type : 'sf-list' + }, + 'sec-ch-ua-form-factor' : { + prop : 'formFactor', + type : 'sf-string' + }, + 'sec-ch-ua-full-version-list' : { + prop : 'fullVersionList', + type : 'sf-list' + }, + 'sec-ch-ua-mobile' : { + prop : 'mobile', + type : 'sf-boolean', + }, + 'sec-ch-ua-model' : { + prop : 'model', + type : 'sf-string', + }, + 'sec-ch-ua-platform' : { + prop : 'platform', + type : 'sf-string' + }, + 'sec-ch-ua-platform-version' : { + prop : 'platformVersion', + type : 'sf-string' + }, + 'sec-ch-ua-wow64' : { + prop : 'wow64', + type : 'sf-boolean' + } +}; + +const UACHParser = (headers) => { + const parse = (str, type) => { + if (!str) { + return ''; + } + switch (type) { + case 'sf-boolean': + return /\?1/.test(str); + case 'sf-list': + return str.replace(/\\?\"/g, '') + .split(', ') + .map(brands => { + const [brand, version] = brands.split(';v='); + return { + brand : brand, + version : version + }; + }); + case 'sf-string': + default: + return str.replace(/\\?\"/g, ''); + } + }; + let ch = {}; + Object.keys(UACHMap).forEach(field => { + if (headers.hasOwnProperty(field)) { + const { prop, type } = UACHMap[field]; + ch[prop] = parse(headers[field], type); + } + }); + return ch; +}; + +module.exports = { + UACHParser +}; \ No newline at end of file diff --git a/src/client-hints-helpers/client-hints-helpers.mjs b/src/client-hints-helpers/client-hints-helpers.mjs new file mode 100644 index 000000000..184e0002d --- /dev/null +++ b/src/client-hints-helpers/client-hints-helpers.mjs @@ -0,0 +1,92 @@ +// Generated ESM version of @ua-parser-js/client-hints-helpers +// DO NOT EDIT THIS FILE! +// Source: /src/client-hints-helpers/client-hints-helpers.js + +//////////////////////////////////////////////////// +/* A collection of utility methods for client-hints + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +/////////////////////////////////////////////////// + +/*jshint esversion: 11 */ + +const UACHMap = { + 'sec-ch-ua-arch' : { + prop : 'architecture', + type : 'sf-string' + }, + 'sec-ch-ua-bitness' : { + prop : 'bitness', + type : 'sf-string' + }, + 'sec-ch-ua' : { + prop : 'brands', + type : 'sf-list' + }, + 'sec-ch-ua-form-factor' : { + prop : 'formFactor', + type : 'sf-string' + }, + 'sec-ch-ua-full-version-list' : { + prop : 'fullVersionList', + type : 'sf-list' + }, + 'sec-ch-ua-mobile' : { + prop : 'mobile', + type : 'sf-boolean', + }, + 'sec-ch-ua-model' : { + prop : 'model', + type : 'sf-string', + }, + 'sec-ch-ua-platform' : { + prop : 'platform', + type : 'sf-string' + }, + 'sec-ch-ua-platform-version' : { + prop : 'platformVersion', + type : 'sf-string' + }, + 'sec-ch-ua-wow64' : { + prop : 'wow64', + type : 'sf-boolean' + } +}; + +const UACHParser = (headers) => { + const parse = (str, type) => { + if (!str) { + return ''; + } + switch (type) { + case 'sf-boolean': + return /\?1/.test(str); + case 'sf-list': + return str.replace(/\\?\"/g, '') + .split(', ') + .map(brands => { + const [brand, version] = brands.split(';v='); + return { + brand : brand, + version : version + }; + }); + case 'sf-string': + default: + return str.replace(/\\?\"/g, ''); + } + }; + let ch = {}; + Object.keys(UACHMap).forEach(field => { + if (headers.hasOwnProperty(field)) { + const { prop, type } = UACHMap[field]; + ch[prop] = parse(headers[field], type); + } + }); + return ch; +}; + +export { + UACHParser +}; \ No newline at end of file diff --git a/src/helpers/package.json b/src/client-hints-helpers/package.json similarity index 62% rename from src/helpers/package.json rename to src/client-hints-helpers/package.json index b5167cfaa..39a2dc2e6 100644 --- a/src/helpers/package.json +++ b/src/client-hints-helpers/package.json @@ -1,13 +1,13 @@ { - "title": "UAParser.js Helpers", - "name": "@ua-parser-js/helpers", - "version": "0.0.3", + "title": "Client-Hints Helpers", + "name": "@ua-parser-js/client-hints-helpers", + "version": "0.0.1", "author": "Faisal Salman ", - "description": "A collection of utility methods for UAParser.js", - "main": "ua-parser-helpers.js", - "module": "ua-parser-helpers.mjs", + "description": "A collection of utility methods for working with client-hints", + "main": "client-hints-helpers.js", + "module": "client-hints-helpers.mjs", "scripts": { - "test": "echo 1" + "test": "mocha ../../test/mocha-test-helpers" }, "repository": { "type": "git", diff --git a/src/client-hints-helpers/readme.md b/src/client-hints-helpers/readme.md new file mode 100644 index 000000000..b1f569e38 --- /dev/null +++ b/src/client-hints-helpers/readme.md @@ -0,0 +1,75 @@ +# @ua-parser-js/client-hints-helpers + +This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client-hints. + +```sh +npm i @ua-parser-js/client-hints-helpers +``` + +### * `UACHParser(headers:object):object` + +Parse user-agent client-hints HTTP headers (sec-ch-ua) into its JS API equivalent + +## Code Example + +```js +import { UACHParser } from '@ua-parser-js/client-hints-helpers'; + +/* + Suppose we're in a server having this client hints data: + + const headers = { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : 'arm', + 'sec-ch-ua-bitness' : '64', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : 'Pixel 99', + 'sec-ch-ua-platform' : 'Linux', + 'sec-ch-ua-platform-version' : '13', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; +*/ + +const userAgentData = UACHParser(headers); + +console.log(userAgentData); +/* + { + "architecture": "arm", + "bitness": "64", + "brands": [ + { + "brand": "Chromium", + "version": "93" + }, + { + "brand": "Google Chrome", + "version": "93" + }, + { + "brand": " Not;A Brand", + "version": "99" + } + ], + "fullVersionList": [ + { + "brand": "Chromium", + "version": "93.0.1.2" + }, + { + "brand": "Google Chrome", + "version": "93.0.1.2" + }, + { + "brand": " Not;A Brand", + "version": "99.0.1.2" + } + ], + "mobile": true, + "model": "Pixel 99", + "platform": "Linux", + "platformVersion": "13" + } +*/ +``` \ No newline at end of file diff --git a/src/helpers/readme.md b/src/helpers/readme.md deleted file mode 100644 index 5f1efd2c8..000000000 --- a/src/helpers/readme.md +++ /dev/null @@ -1,131 +0,0 @@ -# @ua-parser-js/helpers - -This package contains a collection of utility methods for [UAParser.js](https://github.com/faisalman/ua-parser-js) - -```sh -npm i @ua-parser-js/helpers -``` - -### * `isFrozenUA(ua:string):boolean` - -Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/) - -### * `unfreezeUA():Promise` - -Construct new unfreezed user-agent string using real data from client hints - -### * `UACHParser(headers: object): object` - -Parse client hints HTTP headers (sec-ch-ua) into its JS API equivalent - -## Code Example - -```js -import { isFrozenUA } from '@ua-parser-js/helpers'; - -const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36"; -const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36"; - -console.log(isFrozenUA(regularMobileUA)); -// false - -console.log(isFrozenUA(freezedMobileUA)); -// true -``` - -```js -import { unfreezeUA } from '@ua-parser-js/helpers'; - -/* - Suppose we're in a browser having this client hints data: - - { - fullVersionList: [ - { - brand: 'New Browser', - version: '110.1.2.3' - }, - { - brand: 'Chromium', - version: '110.1.2.3' - }, - { - brand: 'Not(A:Brand', - version: '110' - } - ], - platform: 'Windows', - platformVersion: '13.0.0' - } - - And a freezed user-agent: - - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36' -*/ - -unfreezeUA() - .then(ua => console.log(ua)); -// 'Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36' -``` - -```js -import { UACHParser } from '@ua-parser-js/helpers'; - -/* - Suppose we're in a server having this client hints data: - - const headers = { - 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', - 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : 'arm', - 'sec-ch-ua-bitness' : '64', - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : 'Pixel 99', - 'sec-ch-ua-platform' : 'Linux', - 'sec-ch-ua-platform-version' : '13', - 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' - }; -*/ - -const userAgentData = UACHParser(headers); - -console.log(userAgentData); -/* - { - "architecture": "arm", - "bitness": "64", - "brands": [ - { - "brand": "Chromium", - "version": "93" - }, - { - "brand": "Google Chrome", - "version": "93" - }, - { - "brand": " Not;A Brand", - "version": "99" - } - ], - "fullVersionList": [ - { - "brand": "Chromium", - "version": "93.0.1.2" - }, - { - "brand": "Google Chrome", - "version": "93.0.1.2" - }, - { - "brand": " Not;A Brand", - "version": "99.0.1.2" - } - ], - "mobile": true, - "model": "Pixel 99", - "platform": "Linux", - "platformVersion": "13" - } -*/ -``` \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs deleted file mode 100644 index bf0dd6650..000000000 --- a/src/helpers/ua-parser-helpers.mjs +++ /dev/null @@ -1,44 +0,0 @@ -// Generated ESM version of UAParser.js helpers -// DO NOT EDIT THIS FILE! -// Source: /src/helpers/ua-parser-helpers.js - -/////////////////////////////////////////////// -/* Helpers for UAParser.js - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -////////////////////////////////////////////// - -/*jshint esversion: 6 */ - -/* - # Reference: - https://www.chromium.org/updates/ua-reduction/ - - # Desktop - --- - Format: - Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 - - Possible values: - - Windows NT 10.0; Win64; x64 - - Macintosh; Intel Mac OS X 10_15_7 - - X11; Linux x86_64 - - X11; CrOS x86_64 14541.0.0 - - Fuchsia - - # Mobile & Tablet: (except iOS/Android WebView) - --- - Format: - Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 - - Possible values: - - "Mobile" - - "" (empty string for Tablets & Desktop) -*/ - -const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); - -export { - isFrozenUA -}; \ No newline at end of file diff --git a/src/user-agent-helpers/package.json b/src/user-agent-helpers/package.json new file mode 100644 index 000000000..aef3208e2 --- /dev/null +++ b/src/user-agent-helpers/package.json @@ -0,0 +1,32 @@ +{ + "title": "User-Agent Helpers", + "name": "@ua-parser-js/user-agent-helpers", + "version": "0.0.1", + "author": "Faisal Salman ", + "description": "A collection of utility methods for working with user-agent", + "main": "user-agent-helpers.js", + "module": "user-agent-helpers.mjs", + "scripts": { + "test": "mocha ../../test/mocha-test-helpers" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/faisalman/ua-parser-js.git" + }, + "keywords": [ + "ua-parser-js", + "browser-detection", + "device-detection", + "os-detection", + "user-agent", + "client-hints" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/faisalman/ua-parser-js/issues" + }, + "homepage": "https://github.com/faisalman/ua-parser-js#readme", + "dependencies": { + "@ua-parser-js/client-hints-helpers": "*" + } +} diff --git a/src/user-agent-helpers/readme.md b/src/user-agent-helpers/readme.md new file mode 100644 index 000000000..b2eefd25b --- /dev/null +++ b/src/user-agent-helpers/readme.md @@ -0,0 +1,67 @@ +# @ua-parser-js/user-agent-helpers + +This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent. + +```sh +npm i @ua-parser-js/user-agent-helpers +``` + +### * `isFrozenUA(ua:string):boolean` + +Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/) + +### * `unfreezeUA([ua:string,ch:object]|[headers:object]):Promise` + +Construct new unfreezed user-agent string using real data from client hints + +## Code Example + +```js +import { isFrozenUA } from '@ua-parser-js/user-agent-helpers'; + +const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36"; +const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36"; + +console.log(isFrozenUA(regularMobileUA)); +// false + +console.log(isFrozenUA(freezedMobileUA)); +// true +``` + +```js +import { unfreezeUA } from '@ua-parser-js/user-agent-helpers'; + +/* + Suppose we're in a browser having this client hints data: + + { + fullVersionList: [ + { + brand: 'New Browser', + version: '110.1.2.3' + }, + { + brand: 'Chromium', + version: '110.1.2.3' + }, + { + brand: 'Not(A:Brand', + version: '110' + } + ], + platform: 'Windows', + platformVersion: '13.0.0', + architecture: 'arm' + } + + With a frozen user-agent: + + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36' +*/ + +// Now let's generate a complete user-agent: +unfreezeUA() + .then(newUA => console.log(newUA)); +// 'Mozilla/5.0 (Windows NT 11.0; ARM) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36' +``` \ No newline at end of file diff --git a/src/user-agent-helpers/user-agent-helpers.d.ts b/src/user-agent-helpers/user-agent-helpers.d.ts new file mode 100644 index 000000000..ae63f9fe7 --- /dev/null +++ b/src/user-agent-helpers/user-agent-helpers.d.ts @@ -0,0 +1,4 @@ +export function isFrozenUA(ua: string): boolean; +export function unfreezeUA(): Promise; +export function unfreezeUA(ua: string, ch: ClientHintsJSHighEntropy): Promise; +export function unfreezeUA(headers: ClientHintsHTTPHeaders): Promise; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/user-agent-helpers/user-agent-helpers.js similarity index 63% rename from src/helpers/ua-parser-helpers.js rename to src/user-agent-helpers/user-agent-helpers.js index 26d28f726..1b74e1d8e 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/user-agent-helpers/user-agent-helpers.js @@ -1,12 +1,14 @@ -/////////////////////////////////////////////// -/* A collection of utility methods for UAParser.js +//////////////////////////////////////////////////// +/* A collection of utility methods for user-agent https://github.com/faisalman/ua-parser-js Author: Faisal Salman MIT License */ -////////////////////////////////////////////// +/////////////////////////////////////////////////// /*jshint esversion: 11 */ +const { UACHParser } = require('@ua-parser-js/client-hints-helpers'); + /* # Reference: https://www.chromium.org/updates/ua-reduction/ @@ -89,84 +91,7 @@ const unfreezeUA = async (ua, ch) => { return ua; }; -const UACHMap = { - 'sec-ch-ua-arch' : { - prop : 'architecture', - type : 'sf-string' - }, - 'sec-ch-ua-bitness' : { - prop : 'bitness', - type : 'sf-string' - }, - 'sec-ch-ua' : { - prop : 'brands', - type : 'sf-list' - }, - 'sec-ch-ua-form-factor' : { - prop : 'formFactor', - type : 'sf-string' - }, - 'sec-ch-ua-full-version-list' : { - prop : 'fullVersionList', - type : 'sf-list' - }, - 'sec-ch-ua-mobile' : { - prop : 'mobile', - type : 'sf-boolean', - }, - 'sec-ch-ua-model' : { - prop : 'model', - type : 'sf-string', - }, - 'sec-ch-ua-platform' : { - prop : 'platform', - type : 'sf-string' - }, - 'sec-ch-ua-platform-version' : { - prop : 'platformVersion', - type : 'sf-string' - }, - 'sec-ch-ua-wow64' : { - prop : 'wow64', - type : 'sf-boolean' - } -}; - -const UACHParser = (headers) => { - const parse = (str, type) => { - if (!str) { - return ''; - } - switch (type) { - case 'sf-boolean': - return /\?1/.test(str); - case 'sf-list': - return str.replace(/\\?\"/g, '') - .split(', ') - .map(brands => { - const [brand, version] = brands.split(';v='); - return { - brand : brand, - version : version - }; - }); - case 'sf-string': - default: - return str.replace(/\\?\"/g, ''); - } - }; - let ch = {}; - Object.keys(UACHMap).forEach(field => { - if (headers.hasOwnProperty(field)) { - const { prop, type } = UACHMap[field]; - ch[prop] = parse(headers[field], type); - } - }); - return ch; -}; - module.exports = { isFrozenUA, - unfreezeUA, - UACHParser + unfreezeUA }; \ No newline at end of file diff --git a/src/user-agent-helpers/user-agent-helpers.mjs b/src/user-agent-helpers/user-agent-helpers.mjs new file mode 100644 index 000000000..a125a49ae --- /dev/null +++ b/src/user-agent-helpers/user-agent-helpers.mjs @@ -0,0 +1,101 @@ +// Generated ESM version of @ua-parser-js/user-agent-helpers +// DO NOT EDIT THIS FILE! +// Source: /src/user-agent-helpers/user-agent-helpers.js + +//////////////////////////////////////////////////// +/* A collection of utility methods for user-agent + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +/////////////////////////////////////////////////// + +/*jshint esversion: 11 */ + +import { UACHParser } from '@ua-parser-js/client-hints-helpers'; + +/* + # Reference: + https://www.chromium.org/updates/ua-reduction/ + + # Desktop + --- + Format: + Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 + + Possible values: + - Windows NT 10.0; Win64; x64 + - Macintosh; Intel Mac OS X 10_15_7 + - X11; Linux x86_64 + - X11; CrOS x86_64 14541.0.0 + - Fuchsia + + # Mobile & Tablet: (except iOS/Android WebView) + --- + Format: + Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 + + Possible values: + - "Mobile" + - "" (empty string for Tablets & Desktop) +*/ + +const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua); + +const unfreezeUA = async (ua, ch) => { + const env = typeof navigator == 'undefined' ? 'node' : 'browser'; + if (env == 'node') { + if (!ua['user-agent']) { + throw new Error('User-Agent header not found'); + } + ch = UACHParser(ua); + ua = ua['user-agent']; + } else { + ua = ua || navigator.userAgent; + ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']); + } + if (isFrozenUA(ua) && ch) { + switch (ch.platform) { + case 'Windows': + let [major, minor] = ch.platformVersion + .split('.') + .map(num => parseInt(num, 10)); + major = (major < 1) ? '6' : (major >= 13) ? '11' : '10'; + ua = ua .replace(/(?Windows NT) 10\.0/, `$ ${major}.${minor}`) + .replace(/; (?Win64; x64)/, + (ch.architecture == 'arm') ? + '; ARM' : + (ch.wow64) ? + '; WOW64' : + (ch.architecture == 'x86' && ch.bitness != '64') ? + '' : '; $'); + break; + case 'Android': + ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`); + break; + case 'Linux': + case 'Chrome OS': + ua = ua.replace(/(?x86_64)/, + (ch.architecture == 'arm') ? + ((ch.bitness == '64') ? 'arm64' : 'arm') : + (ch.architecture == 'x86' && ch.bitness != '64') ? + 'x86' : '$'); + break; + case 'macOS': + ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`); + break; + } + if (ch.fullVersionList) { + ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /, + ch.fullVersionList + .filter(browser => !/not.a.brand/i.test(browser.brand)) + .map(browser => `${browser.brand.replace(/^google /i,'')}/${browser.version} `) + .join('')); + } + } + return ua; +}; + +export { + isFrozenUA, + unfreezeUA +}; \ No newline at end of file diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index 07ba15c6c..526887dd0 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,4 +1,5 @@ -const { isFrozenUA, unfreezeUA, UACHParser } = require('@ua-parser-js/helpers'); +const { isFrozenUA, unfreezeUA } = require('@ua-parser-js/user-agent-helpers'); +const { UACHParser } = require('@ua-parser-js/client-hints-helpers'); const assert = require('assert'); describe('isFrozenUA()', () => { @@ -54,7 +55,7 @@ describe('unfreezeUA()', () => { }); describe('UACHParser()', () => { - it('parse client hints HTTP headers (sec-ch-ua) into a JavaScript object', () => { + it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => { assert.deepEqual(UACHParser(headers), { "architecture": "arm", "bitness": "64", diff --git a/test/playwright-test-helpers.spec.mjs b/test/playwright-test-helpers.spec.mjs deleted file mode 100644 index c5bc65e70..000000000 --- a/test/playwright-test-helpers.spec.mjs +++ /dev/null @@ -1,47 +0,0 @@ -// @ts-check -import { test, expect } from '@playwright/test'; -import { unfreezeUA } from '@ua-parser-js/helpers'; - -test('test for unfreezeUA() method', async ({ page }) => { - - - await page.addInitScript(() => { - Object.defineProperty(navigator, 'userAgentData', { - value: { - brands: [], - platform: 'Windows', - mobile: false, - getHighEntropyValues: () => { - return Promise.resolve({ - architecture: 'x86', - bitness: '64', - fullVersionList: [ - { - brand: 'New Browser', - version: '110.1.2.3' - }, - { - brand: 'Chromium', - version: '110.1.2.3' - }, - { - brand: 'Not(A:Brand', - version: '110' - } - ], - platform: 'Windows', - platformVersion: '0.3' - }); - } - } - }); - Object.defineProperty(navigator, 'userAgent', { - value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36' - }); - }); - await page.goto('about:blank'); - await page.addScriptTag({ path: './src/helpers/ua-parser-helpers.js' }); - // @ts-ignore - const ua = await page.evaluate(async () => await unfreezeUA()); - expect(ua).toBe('Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'); -}); \ No newline at end of file From f538018f8e4813f1ca96dc8edeb5ab6958444862 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 24 Aug 2023 21:08:33 +0700 Subject: [PATCH 119/388] Update package.json/package-lock.json & remove bower.json (at last!) --- bower.json | 17 ----------------- package-lock.json | 5 ++++- package.json | 9 +++++++-- 3 files changed, 11 insertions(+), 20 deletions(-) delete mode 100644 bower.json diff --git a/bower.json b/bower.json deleted file mode 100644 index a8c0ae384..000000000 --- a/bower.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "ua-parser-js", - "version": "2.0.0-alpha.3", - "authors": [ - "Faisal Salman " - ], - "private": false, - "main": "src/ua-parser.js", - "ignore": [ - "build", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": {} -} diff --git a/package-lock.json b/package-lock.json index 613ed7399..92bbfbdb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3797,7 +3797,10 @@ "src/user-agent-helpers": { "name": "@ua-parser-js/user-agent-helpers", "version": "0.0.1", - "license": "MIT" + "license": "MIT", + "dependencies": { + "@ua-parser-js/client-hints-helpers": "*" + } } } } diff --git a/package.json b/package.json index 205980ee7..f687f886c 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,11 @@ "device", "cpu", "jquery-plugin", - "ecosystem:jquery" + "ecosystem:jquery", + "ua-parser-js", + "browser-detection", + "device-detection", + "os-detection" ], "homepage": "https://github.com/faisalman/ua-parser-js", "contributors": [ @@ -215,6 +219,7 @@ } ], "workspaces": [ - "src/helpers" + "src/client-hints-helpers", + "src/user-agent-helpers" ] } From 15226914260142b0ef96b081565d5f30198ed521 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 26 Aug 2023 04:55:07 +0700 Subject: [PATCH 120/388] [ua-client-hints] Refactor `UAClientHints` --- package-lock.json | 10 +- package.json | 2 +- script/build-module.js | 6 +- .../client-hints-helpers.d.ts | 35 ----- .../client-hints-helpers.js | 88 ------------ .../client-hints-helpers.mjs | 92 ------------ src/client-hints-helpers/readme.md | 75 ---------- .../package.json | 10 +- src/ua-client-hints/readme.md | 128 +++++++++++++++++ src/ua-client-hints/ua-client-hints.d.ts | 39 +++++ src/ua-client-hints/ua-client-hints.js | 130 +++++++++++++++++ src/ua-client-hints/ua-client-hints.mjs | 134 ++++++++++++++++++ test/mocha-test-helpers.js | 28 ++-- 13 files changed, 466 insertions(+), 311 deletions(-) delete mode 100644 src/client-hints-helpers/client-hints-helpers.d.ts delete mode 100644 src/client-hints-helpers/client-hints-helpers.js delete mode 100644 src/client-hints-helpers/client-hints-helpers.mjs delete mode 100644 src/client-hints-helpers/readme.md rename src/{client-hints-helpers => ua-client-hints}/package.json (78%) create mode 100644 src/ua-client-hints/readme.md create mode 100644 src/ua-client-hints/ua-client-hints.d.ts create mode 100644 src/ua-client-hints/ua-client-hints.js create mode 100644 src/ua-client-hints/ua-client-hints.mjs diff --git a/package-lock.json b/package-lock.json index 92bbfbdb4..f2262f30d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ ], "license": "MIT", "workspaces": [ - "src/client-hints-helpers", + "src/ua-client-hints", "src/user-agent-helpers" ], "devDependencies": { @@ -768,6 +768,10 @@ "resolved": "src/client-hints-helpers", "link": true }, + "node_modules/@ua-parser-js/ua-client-hints": { + "resolved": "src/ua-client-hints", + "link": true + }, "node_modules/@ua-parser-js/user-agent-helpers": { "resolved": "src/user-agent-helpers", "link": true @@ -3794,6 +3798,10 @@ "extraneous": true, "license": "MIT" }, + "src/ua-client-hints": { + "version": "0.0.1", + "license": "MIT" + }, "src/user-agent-helpers": { "name": "@ua-parser-js/user-agent-helpers", "version": "0.0.1", diff --git a/package.json b/package.json index f687f886c..e4b620706 100644 --- a/package.json +++ b/package.json @@ -219,7 +219,7 @@ } ], "workspaces": [ - "src/client-hints-helpers", + "src/ua-client-hints", "src/user-agent-helpers" ] } diff --git a/script/build-module.js b/script/build-module.js index 3b00317f6..d3c793556 100755 --- a/script/build-module.js +++ b/script/build-module.js @@ -54,9 +54,9 @@ const modules = [ replacements : [] }, { - src : 'src/client-hints-helpers/client-hints-helpers.js', - dest : 'src/client-hints-helpers/client-hints-helpers.mjs', - title : '@ua-parser-js/client-hints-helpers', + src : 'src/ua-client-hints/ua-client-hints.js', + dest : 'src/ua-client-hints/ua-client-hints.mjs', + title : '@ua-parser-js/ua-client-hints', replacements : [] } ]; diff --git a/src/client-hints-helpers/client-hints-helpers.d.ts b/src/client-hints-helpers/client-hints-helpers.d.ts deleted file mode 100644 index 00f98f84b..000000000 --- a/src/client-hints-helpers/client-hints-helpers.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -interface IBrowser { - brand: string; - version: string; -} - -interface ClientHintsJSLowEntropy { - brands: Array; - mobile: boolean; - platform: string; -} - -export interface ClientHintsJSHighEntropy extends ClientHintsJSLowEntropy { - architecture?: string; - bitness?: string; - formFactor?: string; - fullVersionList?: Array; - model?: string; - platformVersion?: string; - wow64?: boolean; -}; - -export interface ClientHintsHTTPHeaders { - 'sec-ch-ua-arch'?: string; - 'sec-ch-ua-bitness'?: string; - 'sec-ch-ua'?: string; - 'sec-ch-ua-form-factor'?: string; - 'sec-ch-ua-full-version-list'?: string; - 'sec-ch-ua-mobile'?: string; - 'sec-ch-ua-model'?: string; - 'sec-ch-ua-platform'?: string; - 'sec-ch-ua-platform-version'?: string; - 'sec-ch-ua-wow64'?: string; -} - -export function UACHParser(headers: ClientHintsHTTPHeaders): ClientHintsJSHighEntropy; \ No newline at end of file diff --git a/src/client-hints-helpers/client-hints-helpers.js b/src/client-hints-helpers/client-hints-helpers.js deleted file mode 100644 index 5fa3bbc77..000000000 --- a/src/client-hints-helpers/client-hints-helpers.js +++ /dev/null @@ -1,88 +0,0 @@ -//////////////////////////////////////////////////// -/* A collection of utility methods for client-hints - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -/////////////////////////////////////////////////// - -/*jshint esversion: 11 */ - -const UACHMap = { - 'sec-ch-ua-arch' : { - prop : 'architecture', - type : 'sf-string' - }, - 'sec-ch-ua-bitness' : { - prop : 'bitness', - type : 'sf-string' - }, - 'sec-ch-ua' : { - prop : 'brands', - type : 'sf-list' - }, - 'sec-ch-ua-form-factor' : { - prop : 'formFactor', - type : 'sf-string' - }, - 'sec-ch-ua-full-version-list' : { - prop : 'fullVersionList', - type : 'sf-list' - }, - 'sec-ch-ua-mobile' : { - prop : 'mobile', - type : 'sf-boolean', - }, - 'sec-ch-ua-model' : { - prop : 'model', - type : 'sf-string', - }, - 'sec-ch-ua-platform' : { - prop : 'platform', - type : 'sf-string' - }, - 'sec-ch-ua-platform-version' : { - prop : 'platformVersion', - type : 'sf-string' - }, - 'sec-ch-ua-wow64' : { - prop : 'wow64', - type : 'sf-boolean' - } -}; - -const UACHParser = (headers) => { - const parse = (str, type) => { - if (!str) { - return ''; - } - switch (type) { - case 'sf-boolean': - return /\?1/.test(str); - case 'sf-list': - return str.replace(/\\?\"/g, '') - .split(', ') - .map(brands => { - const [brand, version] = brands.split(';v='); - return { - brand : brand, - version : version - }; - }); - case 'sf-string': - default: - return str.replace(/\\?\"/g, ''); - } - }; - let ch = {}; - Object.keys(UACHMap).forEach(field => { - if (headers.hasOwnProperty(field)) { - const { prop, type } = UACHMap[field]; - ch[prop] = parse(headers[field], type); - } - }); - return ch; -}; - -module.exports = { - UACHParser -}; \ No newline at end of file diff --git a/src/client-hints-helpers/client-hints-helpers.mjs b/src/client-hints-helpers/client-hints-helpers.mjs deleted file mode 100644 index 184e0002d..000000000 --- a/src/client-hints-helpers/client-hints-helpers.mjs +++ /dev/null @@ -1,92 +0,0 @@ -// Generated ESM version of @ua-parser-js/client-hints-helpers -// DO NOT EDIT THIS FILE! -// Source: /src/client-hints-helpers/client-hints-helpers.js - -//////////////////////////////////////////////////// -/* A collection of utility methods for client-hints - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -/////////////////////////////////////////////////// - -/*jshint esversion: 11 */ - -const UACHMap = { - 'sec-ch-ua-arch' : { - prop : 'architecture', - type : 'sf-string' - }, - 'sec-ch-ua-bitness' : { - prop : 'bitness', - type : 'sf-string' - }, - 'sec-ch-ua' : { - prop : 'brands', - type : 'sf-list' - }, - 'sec-ch-ua-form-factor' : { - prop : 'formFactor', - type : 'sf-string' - }, - 'sec-ch-ua-full-version-list' : { - prop : 'fullVersionList', - type : 'sf-list' - }, - 'sec-ch-ua-mobile' : { - prop : 'mobile', - type : 'sf-boolean', - }, - 'sec-ch-ua-model' : { - prop : 'model', - type : 'sf-string', - }, - 'sec-ch-ua-platform' : { - prop : 'platform', - type : 'sf-string' - }, - 'sec-ch-ua-platform-version' : { - prop : 'platformVersion', - type : 'sf-string' - }, - 'sec-ch-ua-wow64' : { - prop : 'wow64', - type : 'sf-boolean' - } -}; - -const UACHParser = (headers) => { - const parse = (str, type) => { - if (!str) { - return ''; - } - switch (type) { - case 'sf-boolean': - return /\?1/.test(str); - case 'sf-list': - return str.replace(/\\?\"/g, '') - .split(', ') - .map(brands => { - const [brand, version] = brands.split(';v='); - return { - brand : brand, - version : version - }; - }); - case 'sf-string': - default: - return str.replace(/\\?\"/g, ''); - } - }; - let ch = {}; - Object.keys(UACHMap).forEach(field => { - if (headers.hasOwnProperty(field)) { - const { prop, type } = UACHMap[field]; - ch[prop] = parse(headers[field], type); - } - }); - return ch; -}; - -export { - UACHParser -}; \ No newline at end of file diff --git a/src/client-hints-helpers/readme.md b/src/client-hints-helpers/readme.md deleted file mode 100644 index b1f569e38..000000000 --- a/src/client-hints-helpers/readme.md +++ /dev/null @@ -1,75 +0,0 @@ -# @ua-parser-js/client-hints-helpers - -This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client-hints. - -```sh -npm i @ua-parser-js/client-hints-helpers -``` - -### * `UACHParser(headers:object):object` - -Parse user-agent client-hints HTTP headers (sec-ch-ua) into its JS API equivalent - -## Code Example - -```js -import { UACHParser } from '@ua-parser-js/client-hints-helpers'; - -/* - Suppose we're in a server having this client hints data: - - const headers = { - 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', - 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : 'arm', - 'sec-ch-ua-bitness' : '64', - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : 'Pixel 99', - 'sec-ch-ua-platform' : 'Linux', - 'sec-ch-ua-platform-version' : '13', - 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' - }; -*/ - -const userAgentData = UACHParser(headers); - -console.log(userAgentData); -/* - { - "architecture": "arm", - "bitness": "64", - "brands": [ - { - "brand": "Chromium", - "version": "93" - }, - { - "brand": "Google Chrome", - "version": "93" - }, - { - "brand": " Not;A Brand", - "version": "99" - } - ], - "fullVersionList": [ - { - "brand": "Chromium", - "version": "93.0.1.2" - }, - { - "brand": "Google Chrome", - "version": "93.0.1.2" - }, - { - "brand": " Not;A Brand", - "version": "99.0.1.2" - } - ], - "mobile": true, - "model": "Pixel 99", - "platform": "Linux", - "platformVersion": "13" - } -*/ -``` \ No newline at end of file diff --git a/src/client-hints-helpers/package.json b/src/ua-client-hints/package.json similarity index 78% rename from src/client-hints-helpers/package.json rename to src/ua-client-hints/package.json index 39a2dc2e6..0ea28cdb9 100644 --- a/src/client-hints-helpers/package.json +++ b/src/ua-client-hints/package.json @@ -1,11 +1,11 @@ { - "title": "Client-Hints Helpers", - "name": "@ua-parser-js/client-hints-helpers", + "title": "User-Agent Client Hints", + "name": "@ua-parser-js/ua-client-hints", "version": "0.0.1", "author": "Faisal Salman ", - "description": "A collection of utility methods for working with client-hints", - "main": "client-hints-helpers.js", - "module": "client-hints-helpers.mjs", + "description": "A collection of utility methods for working with user-agent client hints", + "main": "ua-client-hints.js", + "module": "ua-client-hints.mjs", "scripts": { "test": "mocha ../../test/mocha-test-helpers" }, diff --git a/src/ua-client-hints/readme.md b/src/ua-client-hints/readme.md new file mode 100644 index 000000000..253f4ebe7 --- /dev/null +++ b/src/ua-client-hints/readme.md @@ -0,0 +1,128 @@ +# @ua-parser-js/ua-client-hints + +This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client hints. + +```sh +npm i @ua-parser-js/ua-client-hints +``` + +### * `getUAData([props:array]):object` + +Get user-agent client hints values of current instance in form of JS object representation + +### * `setUAData([uaData:object]):UAClientHints` + +Set values of user-agent client hints for the current instance either from navigator.userAgentData or from HTTP headers (Sec-CH-UA-*) + +### * `getSerializedUAData([props:array]):object` + +Get user-agent client hints values of current instance in form of HTTP headers string representation (Sec-CH-UA-*) + +## Code Example + +```js +import { UAClientHints } from '@ua-parser-js/ua-client-hints'; + +/* + Suppose we're in a server having this client hints data: + + const httpHeaders = { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : 'arm', + 'sec-ch-ua-bitness' : '64', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : 'Pixel 99', + 'sec-ch-ua-platform' : 'Linux', + 'sec-ch-ua-platform-version' : '13' + }; +*/ + +const uaCH = new UAClientHints(); +uaCH.setUAData(httpHeaders); +const uaCHData1 = uaCH.getUAData(); +const uaCHData2 = uaCH.getUAData(['architecture', 'bitness']); + +console.log(uaCHData1); +/* + { + "architecture": "arm", + "bitness": "64", + "brands": [ + { + "brand": "Chromium", + "version": "93" + }, + { + "brand": "Google Chrome", + "version": "93" + }, + { + "brand": " Not;A Brand", + "version": "99" + } + ], + "fullVersionList": [ + { + "brand": "Chromium", + "version": "93.0.1.2" + }, + { + "brand": "Google Chrome", + "version": "93.0.1.2" + }, + { + "brand": " Not;A Brand", + "version": "99.0.1.2" + } + ], + "mobile": true, + "model": "Pixel 99", + "platform": "Linux", + "platformVersion": "13", + "wow64": null, + "formFactor": null + } +*/ + +console.log(uaCHData2); +/* + { + "architecture": "arm", + "bitness": "64" + } +*/ + +uaCH.setUAData({ + "wow64" : true, + "formFactor" : "Automotive" +}); + +const headersData1 = uaCH.getSerializedUAData(); +const headersData2 = uaCH.getSerializedUAData(['brand', 'mobile', 'model']); + +console.log(headersData1); +/* + { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : 'arm', + 'sec-ch-ua-bitness' : '64', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : 'Pixel 99', + 'sec-ch-ua-platform' : 'Linux', + 'sec-ch-ua-platform-version' : '13', + 'sec-ch-ua-wow64' : '?1', + 'sec-ch-ua-form-factor' : 'Automotive' + }; +*/ + +console.log(headersData2); +/* + { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : 'Pixel 99' + }; +*/ +``` \ No newline at end of file diff --git a/src/ua-client-hints/ua-client-hints.d.ts b/src/ua-client-hints/ua-client-hints.d.ts new file mode 100644 index 000000000..307765848 --- /dev/null +++ b/src/ua-client-hints/ua-client-hints.d.ts @@ -0,0 +1,39 @@ +export type UABrowser = { + brand: string | null, + version: string | null +}; + +export type UADataType = boolean | string | Array | null; +export type UADataField = + 'brands' | + 'mobile' | + 'platform' | + 'architecture' | + 'bitness' | + 'formFactor' | + 'fullVersionList' | + 'model' | + 'platformVersion' | + 'wow64'; + +export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list'; +export type HeaderField = + 'sec-ch-ua-arch' | + 'sec-ch-ua-bitness' | + 'sec-ch-ua' | + 'sec-ch-ua-form-factor' | + 'sec-ch-ua-full-version-list' | + 'sec-ch-ua-mobile' | + 'sec-ch-ua-model' | + 'sec-ch-ua-platform' | + 'sec-ch-ua-platform-version' | + 'sec-ch-ua-wow64'; + +export class UAClientHints { + #ch: Map; + #parseHeader(str: string, type: HeaderType): UADataType; + #serialize(data: UADataType, type: HeaderType): string; + getSerializedUAData(): Record; + getUAData(props?: Array): Record; + setUAData(uaData: Record | Record): UAClientHints; +}; \ No newline at end of file diff --git a/src/ua-client-hints/ua-client-hints.js b/src/ua-client-hints/ua-client-hints.js new file mode 100644 index 000000000..d4ddc6077 --- /dev/null +++ b/src/ua-client-hints/ua-client-hints.js @@ -0,0 +1,130 @@ +////////////////////////////////////////////// +/* A collection of utility methods for + working with user-agent client hints + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +///////////////////////////////////////////// + +/*jshint esversion: 11 */ + +const fieldType = Object.freeze({ + Boolean : 'sf-boolean', + List : 'sf-list', + String : 'sf-string' +}); + +const uaCHMap = Object.freeze({ + architecture : { + field : 'Sec-CH-UA-Arch', + type : fieldType.String + }, + bitness : { + field : 'Sec-CH-UA-Bitness', + type : fieldType.String + }, + brands : { + field : 'Sec-CH-UA', + type : fieldType.List + }, + formFactor : { + field : 'Sec-CH-UA-Form-Factor', + type : fieldType.String + }, + fullVersionList : { + field : 'Sec-CH-UA-Full-Version-List', + type : fieldType.List + }, + mobile : { + field : 'Sec-CH-UA-Mobile', + type : fieldType.Boolean + }, + model : { + field : 'Sec-CH-UA-Model', + type : fieldType.String + }, + platform : { + field : 'Sec-CH-UA-Platform', + type : fieldType.String + }, + platformVersion : { + field : 'Sec-CH-UA-Platform-Version', + type : fieldType.String + }, + wow64 : { + field : 'Sec-CH-UA-WOW64', + type : fieldType.Boolean + } +}); + +class UAClientHints { + + #uach = new Map(); + + constructor () { + for (const key in uaCHMap) { + this.#uach.set(key, null); + } + return this; + }; + + #parseHeader (str, type) { + if (!str) { + return null; + } + switch (type) { + case fieldType.Boolean: + return /\?1/.test(str); + case fieldType.List: + return str.replace(/\\?\"/g, '') + .split(',') + .map(brands => { + const [brand, version] = brands.trim().split(';v='); + return { + brand : brand, + version : version + }; + }); + case fieldType.String: + return str.replace(/\s*\\?\"\s*/g, ''); + default: + return ''; + } + }; + + #serialize(data, type) { + throw new Error('Not implemented yet'); + //return ''; + } + + getSerializedUAData() { + throw new Error('Not implemented yet'); + //let http = {}; + //return http; + } + + getUAData(props) { + if (props) { + return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)])); + } + return Object.fromEntries(this.#uach); + } + + setUAData(uaDataValues) { + if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) { + for (const val in uaCHMap) { + const { field, type } = uaCHMap[val]; + this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type)); + } + } else { + for (const value in uaDataValues) { + if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]); + } + } + return this; + }; +} + +module.exports = { + UAClientHints +}; \ No newline at end of file diff --git a/src/ua-client-hints/ua-client-hints.mjs b/src/ua-client-hints/ua-client-hints.mjs new file mode 100644 index 000000000..047f5c828 --- /dev/null +++ b/src/ua-client-hints/ua-client-hints.mjs @@ -0,0 +1,134 @@ +// Generated ESM version of @ua-parser-js/ua-client-hints +// DO NOT EDIT THIS FILE! +// Source: /src/ua-client-hints/ua-client-hints.js + +////////////////////////////////////////////// +/* A collection of utility methods for + working with user-agent client hints + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +///////////////////////////////////////////// + +/*jshint esversion: 11 */ + +const fieldType = Object.freeze({ + Boolean : 'sf-boolean', + List : 'sf-list', + String : 'sf-string' +}); + +const uaCHMap = Object.freeze({ + architecture : { + field : 'Sec-CH-UA-Arch', + type : fieldType.String + }, + bitness : { + field : 'Sec-CH-UA-Bitness', + type : fieldType.String + }, + brands : { + field : 'Sec-CH-UA', + type : fieldType.List + }, + formFactor : { + field : 'Sec-CH-UA-Form-Factor', + type : fieldType.String + }, + fullVersionList : { + field : 'Sec-CH-UA-Full-Version-List', + type : fieldType.List + }, + mobile : { + field : 'Sec-CH-UA-Mobile', + type : fieldType.Boolean + }, + model : { + field : 'Sec-CH-UA-Model', + type : fieldType.String + }, + platform : { + field : 'Sec-CH-UA-Platform', + type : fieldType.String + }, + platformVersion : { + field : 'Sec-CH-UA-Platform-Version', + type : fieldType.String + }, + wow64 : { + field : 'Sec-CH-UA-WOW64', + type : fieldType.Boolean + } +}); + +class UAClientHints { + + #uach = new Map(); + + constructor () { + for (const key in uaCHMap) { + this.#uach.set(key, null); + } + return this; + }; + + #parseHeader (str, type) { + if (!str) { + return null; + } + switch (type) { + case fieldType.Boolean: + return /\?1/.test(str); + case fieldType.List: + return str.replace(/\\?\"/g, '') + .split(',') + .map(brands => { + const [brand, version] = brands.trim().split(';v='); + return { + brand : brand, + version : version + }; + }); + case fieldType.String: + return str.replace(/\s*\\?\"\s*/g, ''); + default: + return ''; + } + }; + + #serialize(data, type) { + throw new Error('Not implemented yet'); + //return ''; + } + + getSerializedUAData() { + throw new Error('Not implemented yet'); + //let http = {}; + //return http; + } + + getUAData(props) { + if (props) { + return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)])); + } + return Object.fromEntries(this.#uach); + } + + setUAData(uaDataValues) { + if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) { + for (const val in uaCHMap) { + const { field, type } = uaCHMap[val]; + this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type)); + } + } else { + for (const value in uaDataValues) { + if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]); + } + } + return this; + }; +} + +export { + UAClientHints +}; \ No newline at end of file diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index 526887dd0..2c0b39f05 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,5 +1,5 @@ const { isFrozenUA, unfreezeUA } = require('@ua-parser-js/user-agent-helpers'); -const { UACHParser } = require('@ua-parser-js/client-hints-helpers'); +const { UAClientHints } = require('@ua-parser-js/ua-client-hints'); const assert = require('assert'); describe('isFrozenUA()', () => { @@ -38,12 +38,12 @@ describe('isFrozenUA()', () => { const headers = { 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : 'arm', - 'sec-ch-ua-bitness' : '64', + 'sec-ch-ua-arch' : '"arm"', + 'sec-ch-ua-bitness' : '"64"', 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : 'Pixel 99', - 'sec-ch-ua-platform' : 'Linux', - 'sec-ch-ua-platform-version' : '13', + 'sec-ch-ua-model' : '"Pixel 99"', + 'sec-ch-ua-platform' : '"Linux"', + 'sec-ch-ua-platform-version' : '"13"', 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' }; @@ -54,9 +54,13 @@ describe('unfreezeUA()', () => { }); }); -describe('UACHParser()', () => { +describe('Parse CH Headers', () => { it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => { - assert.deepEqual(UACHParser(headers), { + assert.deepEqual(new UAClientHints().setUAData(headers).getUAData(['architecture', 'bitness']), { + "architecture": "arm", + "bitness": "64" + }); + assert.deepEqual(new UAClientHints().setUAData(headers).getUAData(), { "architecture": "arm", "bitness": "64", "brands": [ @@ -69,7 +73,7 @@ describe('UACHParser()', () => { "version": "93" }, { - "brand": " Not;A Brand", + "brand": "Not;A Brand", "version": "99" } ], @@ -83,14 +87,16 @@ describe('UACHParser()', () => { "version": "93.0.1.2" }, { - "brand": " Not;A Brand", + "brand": "Not;A Brand", "version": "99.0.1.2" } ], + "formFactor": null, "mobile": true, "model": "Pixel 99", "platform": "Linux", - "platformVersion": "13" + "platformVersion": "13", + "wow64": null }); }); }); \ No newline at end of file From 807dcdbded9180c1813f18df0fd8fcaf20897298 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 30 Aug 2023 12:55:05 +0700 Subject: [PATCH 121/388] Add eslint to devDependencies to support latest ES version --- .eslintrc.json | 13 + dist/ua-parser.min.js | 2 +- dist/ua-parser.pack.js | 2 +- package-lock.json | 857 +++++++++++++++++- package.json | 4 +- script/test-all.sh | 1 + src/enums/ua-parser-enums.mjs | 2 +- src/extensions/ua-parser-extensions.mjs | 2 +- src/main/ua-parser.js | 2 +- src/main/ua-parser.mjs | 19 +- src/user-agent-helpers/user-agent-helpers.js | 4 +- src/user-agent-helpers/user-agent-helpers.mjs | 4 +- test/mocha-test.js | 10 +- ...spec.mjs => playwright-test-main.spec.mjs} | 0 14 files changed, 873 insertions(+), 49 deletions(-) create mode 100644 .eslintrc.json rename test/{playwright-test-browser.spec.mjs => playwright-test-main.spec.mjs} (100%) diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..96aa29a4d --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es2021": true, + "node": true + }, + "parserOptions": { + "ecmaVersion": "latest" + }, + "rules": { + } +} diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 46cf21f2f..60de30081 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-alpha.3 Copyright © 2012-2023 Faisal Salman MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(typeof ua===STR_TYPE)userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[FORMFACTOR]){this.set(TYPE,strMapper(uaCH[FORMFACTOR],formFactorMap))}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(typeof ua===STR_TYPE)userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 0ba28a22b..93cd67c45 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.0-alpha.3 Copyright © 2012-2023 Faisal Salman MIT License */ -!function(i,c){"use strict";function e(i){for(var e={},t=0;tS?ki(i,S):i),this}]]).setUA(o),this}zi.VERSION="2.0.0-alpha.3",zi.BROWSER=e([h,m,u]),zi.CPU=e([g]),zi.DEVICE=e([p,o,f,r,v,x,a,k,y]),zi.ENGINE=zi.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=zi),exports.UAParser=zi):typeof define===l&&define.amd?define(function(){return zi}):typeof i!==b&&(i.UAParser=zi);var Ni,Oi=typeof i!==b&&(i.jQuery||i.Zepto);Oi&&!Oi.ua&&(Ni=new zi,Oi.ua=Ni.getResult(),Oi.ua.get=function(){return Ni.getUA()},Oi.ua.set=function(i){Ni.setUA(i);var e,t=Ni.getResult();for(e in t)Oi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,u){"use strict";function e(i){for(var e={},t=0;t_?yi(i,_):i),this}]]).setUA(o),this}Hi.VERSION="2.0.0-alpha.3",Hi.BROWSER=e([h,m,l]),Hi.CPU=e([g]),Hi.DEVICE=e([p,t,f,o,v,a,r,x,k]),Hi.ENGINE=Hi.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Hi),exports.UAParser=Hi):typeof define===c&&define.amd?define(function(){return Hi}):typeof i!==b&&(i.UAParser=Hi);var Pi,Ui=typeof i!==b&&(i.jQuery||i.Zepto);Ui&&!Ui.ua&&(Pi=new Hi,Ui.ua=Pi.getResult(),Ui.ua.get=function(){return Pi.getUA()},Ui.ua.set=function(i){Pi.setUA(i);var e,t=Pi.getResult();for(e in t)Ui.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f2262f30d..4f5336d39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,9 @@ "src/ua-client-hints", "src/user-agent-helpers" ], + "dependencies": { + "eslint": "^8.48.0" + }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", @@ -41,6 +44,14 @@ "node": "*" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -454,6 +465,140 @@ "node": ">=6.9.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -739,6 +884,38 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@playwright/test": { "version": "1.32.3", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz", @@ -782,6 +959,40 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -908,8 +1119,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -987,7 +1197,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1069,6 +1278,14 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1450,8 +1667,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/console-browserify": { "version": "1.1.0", @@ -1480,6 +1696,19 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -1490,7 +1719,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1536,6 +1764,11 @@ "node": ">=4.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, "node_modules/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", @@ -1584,6 +1817,17 @@ "node": ">=0.3.1" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -1685,6 +1929,247 @@ "node": ">=0.8.0" } }, + "node_modules/eslint": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1698,6 +2183,44 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -1716,6 +2239,40 @@ "node": ">=6" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1738,7 +2295,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -1759,6 +2315,23 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -1846,8 +2419,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/gauge": { "version": "4.0.4", @@ -1955,7 +2527,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1987,7 +2558,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2010,6 +2580,11 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -2082,11 +2657,41 @@ } ] }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2095,8 +2700,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -2120,7 +2724,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2138,7 +2741,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2155,6 +2757,14 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -2173,8 +2783,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", @@ -2310,6 +2919,16 @@ "jshint": "bin/jshint" } }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -2334,11 +2953,22 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -2361,6 +2991,11 @@ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "node_modules/log-symbols": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", @@ -2536,7 +3171,6 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2765,8 +3399,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/napi-build-utils": { "version": "1.0.2", @@ -2774,6 +3407,11 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "dev": true }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, "node_modules/node-abi": { "version": "3.40.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz", @@ -2865,16 +3503,30 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -2889,7 +3541,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -2909,11 +3560,21 @@ "node": ">=6" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -2922,11 +3583,18 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -2983,6 +3651,14 @@ "node": ">=10" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -3010,6 +3686,33 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3104,6 +3807,14 @@ "node": ">=0.4.0" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -3113,11 +3824,19 @@ "node": ">= 4" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -3128,6 +3847,28 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3181,6 +3922,25 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -3401,6 +4161,11 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -3446,6 +4211,28 @@ "node": "*" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/uglify-js": { "version": "3.12.8", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", @@ -3497,6 +4284,14 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -3513,7 +4308,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -3597,8 +4391,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/y18n": { "version": "4.0.3", @@ -3779,7 +4572,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, "engines": { "node": ">=10" }, @@ -3799,6 +4591,7 @@ "license": "MIT" }, "src/ua-client-hints": { + "name": "@ua-parser-js/ua-client-hints", "version": "0.0.1", "license": "MIT" }, diff --git a/package.json b/package.json index e4b620706..3203ace8e 100644 --- a/package.json +++ b/package.json @@ -171,7 +171,8 @@ "build": "./script/build-dist.sh && ./script/build-module.js", "fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync", "test": "./script/test-all.sh", - "test:jshint": "jshint src && jshint script", + "test:eslint": "eslint src && eslint script", + "test:jshint": "jshint src/main", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", "test:mocha": "mocha -R list test/mocha*js", "test:playwright": "playwright test" @@ -181,6 +182,7 @@ "@babel/traverse": "7.15.4", "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", + "eslint": "^8.48.0", "jshint": "~2.13.6", "mocha": "~8.2.0", "requirejs": "2.3.2", diff --git a/script/test-all.sh b/script/test-all.sh index b47652bda..c8b2ca29b 100755 --- a/script/test-all.sh +++ b/script/test-all.sh @@ -9,6 +9,7 @@ echo ' - lint js code ' npm run test:jshint || exit 1 +npm run test:eslint || exit 1 echo ' - test using mocha diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 6479043bc..702119a55 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -1,4 +1,4 @@ -// Generated ESM version of UAParser.js enums +// Generated ESM version of ua-parser-js/enums // DO NOT EDIT THIS FILE! // Source: /src/enums/ua-parser-enums.js diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 97e07f183..60e7ce16c 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -1,4 +1,4 @@ -// Generated ESM version of UAParser.js extensions +// Generated ESM version of ua-parser-js/extensions // DO NOT EDIT THIS FILE! // Source: /src/extensions/ua-parser-extensions.js diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7047e8327..89fa07f1c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -57,7 +57,7 @@ CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS], + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 6f716d620..85c379a06 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -1,4 +1,4 @@ -// Generated ESM version of UAParser.js +// Generated ESM version of ua-parser-js // DO NOT EDIT THIS FILE! // Source: /src/main/ua-parser.js @@ -45,6 +45,7 @@ USER_AGENT = 'user-agent', UA_MAX_LENGTH = 350, BRANDS = 'brands', + FORMFACTOR = 'formFactor', FULLVERLIST = 'fullVersionList', PLATFORM = 'platform', PLATFORMVER = 'platformVersion', @@ -53,11 +54,12 @@ CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, + CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor', CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS], + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', @@ -258,6 +260,15 @@ '8.1' : 'NT 6.3', '10' : ['NT 6.4', 'NT 10.0'], 'RT' : 'ARM' + }, + + formFactorMap = { + 'embedded' : 'Automotive', + 'mobile' : 'Mobile', + 'tablet' : 'Tablet', + 'smarttv' : 'TV', + 'wearable' : ['VR', 'XR'], + '?' : 'Unknown' }; ////////////// @@ -932,6 +943,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], + [FORMFACTOR, stripQuotes(uach[CH_HEADER_FORM_FACTOR])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1046,6 +1058,9 @@ if (uaCH[MODEL]) { this.set(MODEL, uaCH[MODEL]); } + if (uaCH[FORMFACTOR]) { + this.set(TYPE, strMapper(uaCH[FORMFACTOR], formFactorMap)); + } break; case UA_OS: var osName = uaCH[PLATFORM]; diff --git a/src/user-agent-helpers/user-agent-helpers.js b/src/user-agent-helpers/user-agent-helpers.js index 1b74e1d8e..cfc9ecf07 100644 --- a/src/user-agent-helpers/user-agent-helpers.js +++ b/src/user-agent-helpers/user-agent-helpers.js @@ -7,7 +7,7 @@ /*jshint esversion: 11 */ -const { UACHParser } = require('@ua-parser-js/client-hints-helpers'); +const { UAClientHints } = require('@ua-parser-js/ua-client-hints'); /* # Reference: @@ -43,7 +43,7 @@ const unfreezeUA = async (ua, ch) => { if (!ua['user-agent']) { throw new Error('User-Agent header not found'); } - ch = UACHParser(ua); + ch = new UAClientHints().setUAData(ua).getUAData(); ua = ua['user-agent']; } else { ua = ua || navigator.userAgent; diff --git a/src/user-agent-helpers/user-agent-helpers.mjs b/src/user-agent-helpers/user-agent-helpers.mjs index a125a49ae..b8f398e09 100644 --- a/src/user-agent-helpers/user-agent-helpers.mjs +++ b/src/user-agent-helpers/user-agent-helpers.mjs @@ -11,7 +11,7 @@ /*jshint esversion: 11 */ -import { UACHParser } from '@ua-parser-js/client-hints-helpers'; +import { UAClientHints } from '@ua-parser-js/ua-client-hints'; /* # Reference: @@ -47,7 +47,7 @@ const unfreezeUA = async (ua, ch) => { if (!ua['user-agent']) { throw new Error('User-Agent header not found'); } - ch = UACHParser(ua); + ch = new UAClientHints().setUAData(ua).getUAData(); ua = ua['user-agent']; } else { ua = ua || navigator.userAgent; diff --git a/test/mocha-test.js b/test/mocha-test.js index e1db526ab..7c4cbde11 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -345,12 +345,12 @@ describe('Map UA-CH headers', function () { const headers = { 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : 'ARM', - 'sec-ch-ua-bitness' : '64', + 'sec-ch-ua-arch' : '"arm"', + 'sec-ch-ua-bitness' : '"64"', 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : 'Pixel 99', - 'sec-ch-ua-platform' : 'Windows', - 'sec-ch-ua-platform-version' : '13', + 'sec-ch-ua-model' : '"Pixel 99"', + 'sec-ch-ua-platform' : '"Windows"', + 'sec-ch-ua-platform-version' : '"13"', 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' }; diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-main.spec.mjs similarity index 100% rename from test/playwright-test-browser.spec.mjs rename to test/playwright-test-main.spec.mjs From 05a98aceda2e549db5c82eb04d1c827d8f4ed01f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 3 Sep 2023 01:03:36 +0700 Subject: [PATCH 122/388] Add new package: `gpu-detect` to obtain GPU info from user-agent --- package.json | 1 + script/build-module.js | 6 ++ src/gpu-detect/gpu-detect.d.ts | 3 + src/gpu-detect/gpu-detect.js | 116 +++++++++++++++++++++++++++++++++ src/gpu-detect/package.json | 25 +++++++ src/gpu-detect/readme.md | 16 +++++ src/gpu-detect/test/index.js | 4 ++ 7 files changed, 171 insertions(+) create mode 100644 src/gpu-detect/gpu-detect.d.ts create mode 100644 src/gpu-detect/gpu-detect.js create mode 100644 src/gpu-detect/package.json create mode 100644 src/gpu-detect/readme.md create mode 100644 src/gpu-detect/test/index.js diff --git a/package.json b/package.json index 3203ace8e..1bff1d7f1 100644 --- a/package.json +++ b/package.json @@ -221,6 +221,7 @@ } ], "workspaces": [ + "src/gpu-detect", "src/ua-client-hints", "src/user-agent-helpers" ] diff --git a/script/build-module.js b/script/build-module.js index d3c793556..2c0c4587d 100755 --- a/script/build-module.js +++ b/script/build-module.js @@ -47,6 +47,12 @@ const modules = [ title : 'ua-parser-js/extensions', replacements : [] }, + { + src : 'src/gpu-detect/gpu-detect.js', + dest : 'src/gpu-detect/gpu-detect.mjs', + title : '@ua-parser-js/gpu-detect', + replacements : [] + }, { src : 'src/user-agent-helpers/user-agent-helpers.js', dest : 'src/user-agent-helpers/user-agent-helpers.mjs', diff --git a/src/gpu-detect/gpu-detect.d.ts b/src/gpu-detect/gpu-detect.d.ts new file mode 100644 index 000000000..bccc81b6c --- /dev/null +++ b/src/gpu-detect/gpu-detect.d.ts @@ -0,0 +1,3 @@ +export class GPUDetect { + static getGPU: { vendor: string, model: string } +} \ No newline at end of file diff --git a/src/gpu-detect/gpu-detect.js b/src/gpu-detect/gpu-detect.js new file mode 100644 index 000000000..326dbcf43 --- /dev/null +++ b/src/gpu-detect/gpu-detect.js @@ -0,0 +1,116 @@ +////////////////////////////////////////////// +/* Extracts GPU information from user-agent + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + MIT License */ +///////////////////////////////////////////// + +/*jshint esversion: 11 */ + +const rendererMap = [ + [[ + /(intel).*\b(hd\sgraphics\s\d{4}|iris(?:\spro)|gma\s\w+)/i, // Intel + /(nvidia)\s(geforce\s(?:gtx?\s)\d\w+|quadro)/i, // NVIDIA + /\b(sis)\s(\w+)/i // SiS + ], ['vendor', 'model']], + + [[ + /\b(radeon[\shdr\d]+\w{4,5})/i // ATI/AMD + ], ['model', ['vendor', 'AMD']]], + + [[ + /(adreno\s(?:\(tm\)\s)\w+)/i // Qualcomm + ], [['model', /\(tm\)\s/i, ''], ['vendor', 'Qualcomm']]] +]; + +const vendorMap = [ + [[ + /\b(amd|apple|arm|ati|img|intel|nvidia|qualcomm|samsung|sis)\b/i + ], ['vendor']] +]; + +class RegexMap { + + static parse(str, mapper) { + let res = {}; + if (typeof str === 'string') { + for (const [regs, props] of mapper) { + if (!Array.isArray(regs)) { + throw new Error('RegexMap: Expect Array of RegExp'); + } + if (!Array.isArray(props)) { + throw new Error('RegexMap: Expect Array for Properties Mapping'); + } + for (const reg of regs) { + if (!reg instanceof RegExp) { + throw new Error('RegexMap: Expect RegExp Instance'); + } + const matches = reg.exec(str); + if (matches) { + props.forEach((prop, idx) => { + const val = matches[idx+1]; + if (Array.isArray(prop)) { + const key = prop[0]; + if (typeof key !== 'string') { + throw new Error('RegexMap: Expect String Input'); + } + if (prop.length == 2) { + if (typeof prop[1] === 'string') { + res[key] = prop[1]; + } else if (typeof prop[1] === 'function') { + res[key] = prop[1].call(res, val); + } + } else if (prop.length == 3) { + if (prop[1] instanceof RegExp) { + res[key] = val.replace(prop[1], prop[2]); + } else if (typeof prop[1] === 'function') { + res[key] = prop[1].call(res, val, prop[2]); + } + } else if (prop.length == 4) { + res[key] = prop[3].call(res, val.replace(prop[1], prop[2])); + } else { + res[key] = val; + } + } else if (typeof prop === 'string') { + res[prop] = val; + } + }); + if (res) return res; + } + } + } + } + return res; + }; +} + +class GPUDetect { + + static getGPU (strRenderer, strVendor) { + + let gpuInfo = { vendor : undefined, model : undefined }; + + if (typeof strRenderer !== 'string') { + if (globalThis.document) { + const canvas = document.createElement('canvas'); + const gl = canvas.getContext('webgl2') || + canvas.getContext('webgl') || + canvas.getContext('experimental-webgl'); + if (gl) { + const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); + strVendor = gl.getParameter(debugInfo?.UNMASKED_VENDOR_WEBGL); + strRenderer = gl.getParameter(debugInfo?.UNMASKED_RENDERER_WEBGL); + } + } + } + if (strRenderer || strVendor) { + ({ vendor : gpuInfo.vendor, model : gpuInfo.model } = RegexMap.parse(strRenderer, rendererMap)); + gpuInfo.vendor = gpuInfo.vendor ?? RegexMap.parse(strVendor, rendererMap)?.vendor ?? RegexMap.parse(strVendor, vendorMap)?.vendor; + } + return gpuInfo; + } +} + +module.exports = { + GPUDetect +} \ No newline at end of file diff --git a/src/gpu-detect/package.json b/src/gpu-detect/package.json new file mode 100644 index 000000000..c3ffaf9f5 --- /dev/null +++ b/src/gpu-detect/package.json @@ -0,0 +1,25 @@ +{ + "title": "User-Agent GPU Info", + "name": "@ua-parser-js/gpu-detect", + "version": "0.0.1", + "author": "Faisal Salman ", + "description": "Extracts GPU information from user-agent", + "main": "gpu-detect.js", + "module": "gpu-detect.mjs", + "scripts": { + "test": "mocha ../../test/mocha-test-helpers" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/faisalman/ua-parser-js.git" + }, + "keywords": [ + "ua-parser-js", + "gpu-detection" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/faisalman/ua-parser-js/issues" + }, + "homepage": "https://github.com/faisalman/ua-parser-js#readme" +} diff --git a/src/gpu-detect/readme.md b/src/gpu-detect/readme.md new file mode 100644 index 000000000..86b9fa45f --- /dev/null +++ b/src/gpu-detect/readme.md @@ -0,0 +1,16 @@ +# @ua-parser-js/gpu-detect + +This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that extracts GPU information from user-agent. + +```sh +npm i @ua-parser-js/gpu-detect +``` + +## Code Example + +// in browser environment +const { vendor, model } = GPUDetect.getGPU(); + +// in non-browser environment +const { vendor, model } = GPUDetect.getGPU("AMD Radeon"); +``` \ No newline at end of file diff --git a/src/gpu-detect/test/index.js b/src/gpu-detect/test/index.js new file mode 100644 index 000000000..76b7e7046 --- /dev/null +++ b/src/gpu-detect/test/index.js @@ -0,0 +1,4 @@ +const { GPUDetect } = require('../gpu-detect.js'); + +console.log(GPUDetect.getGPU('AMD Radeon R9 M295X OpenGL Engine')); +console.log(GPUDetect.getGPU('','ATI Technologies Inc.')); \ No newline at end of file From 647e115a1eff4aa65a60079ed18969bd23e67ffa Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 4 Sep 2023 12:28:11 +0700 Subject: [PATCH 123/388] [ua-helpers] Fix type error --- src/user-agent-helpers/package.json | 4 +- src/user-agent-helpers/readme.md | 10 ++++ .../user-agent-helpers/test/index.js | 56 ++----------------- .../user-agent-helpers.d.ts | 35 +++++++++++- 4 files changed, 50 insertions(+), 55 deletions(-) rename test/mocha-test-helpers.js => src/user-agent-helpers/test/index.js (62%) diff --git a/src/user-agent-helpers/package.json b/src/user-agent-helpers/package.json index aef3208e2..ba573084f 100644 --- a/src/user-agent-helpers/package.json +++ b/src/user-agent-helpers/package.json @@ -1,13 +1,13 @@ { "title": "User-Agent Helpers", "name": "@ua-parser-js/user-agent-helpers", - "version": "0.0.1", + "version": "0.0.2", "author": "Faisal Salman ", "description": "A collection of utility methods for working with user-agent", "main": "user-agent-helpers.js", "module": "user-agent-helpers.mjs", "scripts": { - "test": "mocha ../../test/mocha-test-helpers" + "test": "mocha ./test/*" }, "repository": { "type": "git", diff --git a/src/user-agent-helpers/readme.md b/src/user-agent-helpers/readme.md index b2eefd25b..472070765 100644 --- a/src/user-agent-helpers/readme.md +++ b/src/user-agent-helpers/readme.md @@ -64,4 +64,14 @@ import { unfreezeUA } from '@ua-parser-js/user-agent-helpers'; unfreezeUA() .then(newUA => console.log(newUA)); // 'Mozilla/5.0 (Windows NT 11.0; ARM) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36' + +/* +// Alternatively: +const ua = navigator.userAgent; +const ch = await navigator.userAgentData.getHighEntropyValues(); +const newUA = await unfreezeUA(ua, ch); + +// Server environment: +const newUA = await unfreezeUA(req.headers); +*/ ``` \ No newline at end of file diff --git a/test/mocha-test-helpers.js b/src/user-agent-helpers/test/index.js similarity index 62% rename from test/mocha-test-helpers.js rename to src/user-agent-helpers/test/index.js index 2c0b39f05..4cea95409 100644 --- a/test/mocha-test-helpers.js +++ b/src/user-agent-helpers/test/index.js @@ -1,4 +1,4 @@ -const { isFrozenUA, unfreezeUA } = require('@ua-parser-js/user-agent-helpers'); +const { isFrozenUA, unfreezeUA } = require('../user-agent-helpers'); const { UAClientHints } = require('@ua-parser-js/ua-client-hints'); const assert = require('assert'); @@ -35,7 +35,8 @@ describe('isFrozenUA()', () => { }); }); -const headers = { +const req = { + headers : { 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', 'sec-ch-ua-arch' : '"arm"', @@ -45,58 +46,11 @@ const headers = { 'sec-ch-ua-platform' : '"Linux"', 'sec-ch-ua-platform-version' : '"13"', 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' -}; +}}; describe('unfreezeUA()', () => { it('returns an unfreezed user-agent using real data from client hints HTTP headers (sec-ch-ua)', async () => { - const unfreezed = await unfreezeUA(headers); + const unfreezed = await unfreezeUA(req.headers); assert.strictEqual(unfreezed, 'Mozilla/5.0 (X11; Linux arm64) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/93.0.1.2 Chrome/93.0.1.2 Safari/537.36'); }); -}); - -describe('Parse CH Headers', () => { - it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => { - assert.deepEqual(new UAClientHints().setUAData(headers).getUAData(['architecture', 'bitness']), { - "architecture": "arm", - "bitness": "64" - }); - assert.deepEqual(new UAClientHints().setUAData(headers).getUAData(), { - "architecture": "arm", - "bitness": "64", - "brands": [ - { - "brand": "Chromium", - "version": "93" - }, - { - "brand": "Google Chrome", - "version": "93" - }, - { - "brand": "Not;A Brand", - "version": "99" - } - ], - "fullVersionList": [ - { - "brand": "Chromium", - "version": "93.0.1.2" - }, - { - "brand": "Google Chrome", - "version": "93.0.1.2" - }, - { - "brand": "Not;A Brand", - "version": "99.0.1.2" - } - ], - "formFactor": null, - "mobile": true, - "model": "Pixel 99", - "platform": "Linux", - "platformVersion": "13", - "wow64": null - }); - }); }); \ No newline at end of file diff --git a/src/user-agent-helpers/user-agent-helpers.d.ts b/src/user-agent-helpers/user-agent-helpers.d.ts index ae63f9fe7..d77af20c8 100644 --- a/src/user-agent-helpers/user-agent-helpers.d.ts +++ b/src/user-agent-helpers/user-agent-helpers.d.ts @@ -1,4 +1,35 @@ +export type UABrowser = { + brand: string | null, + version: string | null +}; + +export type UADataType = boolean | string | Array | null; +export type UADataField = + 'brands' | + 'mobile' | + 'platform' | + 'architecture' | + 'bitness' | + 'formFactor' | + 'fullVersionList' | + 'model' | + 'platformVersion' | + 'wow64'; + +export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list'; +export type HeaderField = + 'sec-ch-ua-arch' | + 'sec-ch-ua-bitness' | + 'sec-ch-ua' | + 'sec-ch-ua-form-factor' | + 'sec-ch-ua-full-version-list' | + 'sec-ch-ua-mobile' | + 'sec-ch-ua-model' | + 'sec-ch-ua-platform' | + 'sec-ch-ua-platform-version' | + 'sec-ch-ua-wow64'; + export function isFrozenUA(ua: string): boolean; export function unfreezeUA(): Promise; -export function unfreezeUA(ua: string, ch: ClientHintsJSHighEntropy): Promise; -export function unfreezeUA(headers: ClientHintsHTTPHeaders): Promise; \ No newline at end of file +export function unfreezeUA(ua: string, ch: Record): Promise; +export function unfreezeUA(headers: Record): Promise; \ No newline at end of file From a661ab61d5e06df2c423edc3c9256f304ce14d52 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 4 Sep 2023 22:41:58 +0700 Subject: [PATCH 124/388] [ua-client-hints] update test file --- src/ua-client-hints/package.json | 2 +- src/ua-client-hints/test/index.js | 64 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/ua-client-hints/test/index.js diff --git a/src/ua-client-hints/package.json b/src/ua-client-hints/package.json index 0ea28cdb9..9fdb8eaf7 100644 --- a/src/ua-client-hints/package.json +++ b/src/ua-client-hints/package.json @@ -7,7 +7,7 @@ "main": "ua-client-hints.js", "module": "ua-client-hints.mjs", "scripts": { - "test": "mocha ../../test/mocha-test-helpers" + "test": "mocha ./test/*" }, "repository": { "type": "git", diff --git a/src/ua-client-hints/test/index.js b/src/ua-client-hints/test/index.js new file mode 100644 index 000000000..23d126245 --- /dev/null +++ b/src/ua-client-hints/test/index.js @@ -0,0 +1,64 @@ +const { UAClientHints } = require('../ua-client-hints'); +const assert = require('assert'); + +describe('Parse CH Headers', () => { + it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => { + + const req = { + headers : { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : '"arm"', + 'sec-ch-ua-bitness' : '"64"', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : '"Pixel 99"', + 'sec-ch-ua-platform' : '"Linux"', + 'sec-ch-ua-platform-version' : '"13"', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }}; + + assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(['architecture', 'bitness']), { + "architecture": "arm", + "bitness": "64" + }); + + assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(), { + "architecture": "arm", + "bitness": "64", + "brands": [ + { + "brand": "Chromium", + "version": "93" + }, + { + "brand": "Google Chrome", + "version": "93" + }, + { + "brand": "Not;A Brand", + "version": "99" + } + ], + "fullVersionList": [ + { + "brand": "Chromium", + "version": "93.0.1.2" + }, + { + "brand": "Google Chrome", + "version": "93.0.1.2" + }, + { + "brand": "Not;A Brand", + "version": "99.0.1.2" + } + ], + "formFactor": null, + "mobile": true, + "model": "Pixel 99", + "platform": "Linux", + "platformVersion": "13", + "wow64": null + }); + }); +}); \ No newline at end of file From 385e0aaee53e474899e7834d0e1d1926b5cc3591 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 4 Sep 2023 22:42:52 +0700 Subject: [PATCH 125/388] Regenerate lockfile --- package-lock.json | 161 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 135 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4f5336d39..a3c6c31ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,17 +23,16 @@ ], "license": "MIT", "workspaces": [ + "src/gpu-detect", "src/ua-client-hints", "src/user-agent-helpers" ], - "dependencies": { - "eslint": "^8.48.0" - }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", + "eslint": "^8.48.0", "jshint": "~2.13.6", "mocha": "~8.2.0", "requirejs": "2.3.2", @@ -48,6 +47,7 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -469,6 +469,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -483,6 +484,7 @@ "version": "4.8.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", + "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -491,6 +493,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -512,12 +515,14 @@ "node_modules/@eslint/eslintrc/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.21.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -532,6 +537,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -543,6 +549,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -554,6 +561,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { "node": ">=8" }, @@ -565,6 +573,7 @@ "version": "8.48.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -573,6 +582,7 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -586,6 +596,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "engines": { "node": ">=12.22" }, @@ -597,7 +608,8 @@ "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", @@ -888,6 +900,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -900,6 +913,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "engines": { "node": ">= 8" } @@ -908,6 +922,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -945,6 +960,10 @@ "resolved": "src/client-hints-helpers", "link": true }, + "node_modules/@ua-parser-js/gpu-detect": { + "resolved": "src/gpu-detect", + "link": true + }, "node_modules/@ua-parser-js/ua-client-hints": { "resolved": "src/ua-client-hints", "link": true @@ -963,6 +982,7 @@ "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -974,6 +994,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -982,6 +1003,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1119,7 +1141,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/base64-js": { "version": "1.5.1", @@ -1197,6 +1220,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1282,6 +1306,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -1667,7 +1692,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/console-browserify": { "version": "1.1.0", @@ -1700,6 +1726,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1719,6 +1746,7 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1767,7 +1795,8 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/default-require-extensions": { "version": "3.0.1", @@ -1821,6 +1850,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, "dependencies": { "esutils": "^2.0.2" }, @@ -1933,6 +1963,7 @@ "version": "8.48.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -1986,6 +2017,7 @@ "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -2001,6 +2033,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2012,6 +2045,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -2020,6 +2054,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -2033,12 +2068,14 @@ "node_modules/eslint/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2054,6 +2091,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2064,12 +2102,14 @@ "node_modules/eslint/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { "node": ">=10" }, @@ -2081,6 +2121,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -2092,6 +2133,7 @@ "version": "13.21.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -2106,6 +2148,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -2114,6 +2157,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -2125,6 +2169,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2136,6 +2181,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2147,6 +2193,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -2158,6 +2205,7 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -2187,6 +2235,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -2198,6 +2247,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -2209,6 +2259,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -2217,6 +2268,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2242,22 +2294,26 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -2266,6 +2322,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -2295,6 +2352,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -2319,6 +2377,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, "dependencies": { "flatted": "^3.1.0", "rimraf": "^3.0.2" @@ -2330,7 +2389,8 @@ "node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true }, "node_modules/follow-redirects": { "version": "1.15.2", @@ -2419,7 +2479,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/gauge": { "version": "4.0.4", @@ -2527,6 +2588,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2558,6 +2620,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2583,7 +2646,8 @@ "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, "node_modules/growl": { "version": "1.10.5", @@ -2661,6 +2725,7 @@ "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, "engines": { "node": ">= 4" } @@ -2669,6 +2734,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2684,6 +2750,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { "node": ">=0.8.19" } @@ -2692,6 +2759,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2700,7 +2768,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "1.3.8", @@ -2724,6 +2793,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2741,6 +2811,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2761,6 +2832,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -2783,7 +2855,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", @@ -2922,12 +2995,14 @@ "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "node_modules/json5": { "version": "2.2.3", @@ -2957,6 +3032,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -2969,6 +3045,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -2994,7 +3071,8 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, "node_modules/log-symbols": { "version": "4.0.0", @@ -3171,6 +3249,7 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3399,7 +3478,8 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/napi-build-utils": { "version": "1.0.2", @@ -3410,7 +3490,8 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "node_modules/node-abi": { "version": "3.40.0", @@ -3503,6 +3584,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -3511,6 +3593,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -3527,6 +3610,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -3541,6 +3625,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -3564,6 +3649,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -3575,6 +3661,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { "node": ">=8" } @@ -3583,6 +3670,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -3591,6 +3679,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -3655,6 +3744,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "engines": { "node": ">= 0.8.0" } @@ -3690,6 +3780,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, "engines": { "node": ">=6" } @@ -3698,6 +3789,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -3811,6 +3903,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } @@ -3828,6 +3921,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -3837,6 +3931,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -3851,6 +3946,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -3926,6 +4022,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -3937,6 +4034,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -4164,7 +4262,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/tmp": { "version": "0.2.1", @@ -4215,6 +4314,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -4226,6 +4326,7 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, "engines": { "node": ">=10" }, @@ -4288,6 +4389,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -4308,6 +4410,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -4391,7 +4494,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/y18n": { "version": "4.0.3", @@ -4572,6 +4676,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "engines": { "node": ">=10" }, @@ -4584,6 +4689,10 @@ "version": "0.0.1", "license": "MIT" }, + "src/gpu-detect": { + "version": "0.0.1", + "license": "MIT" + }, "src/helpers": { "name": "@ua-parser-js/helpers", "version": "0.0.3", @@ -4597,7 +4706,7 @@ }, "src/user-agent-helpers": { "name": "@ua-parser-js/user-agent-helpers", - "version": "0.0.1", + "version": "0.0.2", "license": "MIT", "dependencies": { "@ua-parser-js/client-hints-helpers": "*" From 52263613480470541a8f8084ac7e30dc8aaef468 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 9 Sep 2023 19:32:28 +0700 Subject: [PATCH 126/388] Remove sub-packages since they've been moved to their own dedicated repo https://github.com/faisalman/ua-client-hints-js https://github.com/faisalman/gpu-detect-js https://github.com/faisalman/ua-is-frozen https://github.com/faisalman/re-parse-js --- .eslintrc.json | 13 -- package.json | 6 - script/build-module.js | 18 --- script/test-all.sh | 2 +- src/gpu-detect/gpu-detect.d.ts | 3 - src/gpu-detect/gpu-detect.js | 116 --------------- src/gpu-detect/package.json | 25 ---- src/gpu-detect/readme.md | 16 --- src/gpu-detect/test/index.js | 4 - src/ua-client-hints/package.json | 29 ---- src/ua-client-hints/readme.md | 128 ----------------- src/ua-client-hints/test/index.js | 64 --------- src/ua-client-hints/ua-client-hints.d.ts | 39 ----- src/ua-client-hints/ua-client-hints.js | 130 ----------------- src/ua-client-hints/ua-client-hints.mjs | 134 ------------------ src/user-agent-helpers/package.json | 32 ----- src/user-agent-helpers/readme.md | 77 ---------- src/user-agent-helpers/test/index.js | 56 -------- .../user-agent-helpers.d.ts | 35 ----- src/user-agent-helpers/user-agent-helpers.js | 97 ------------- src/user-agent-helpers/user-agent-helpers.mjs | 101 ------------- 21 files changed, 1 insertion(+), 1124 deletions(-) delete mode 100644 .eslintrc.json delete mode 100644 src/gpu-detect/gpu-detect.d.ts delete mode 100644 src/gpu-detect/gpu-detect.js delete mode 100644 src/gpu-detect/package.json delete mode 100644 src/gpu-detect/readme.md delete mode 100644 src/gpu-detect/test/index.js delete mode 100644 src/ua-client-hints/package.json delete mode 100644 src/ua-client-hints/readme.md delete mode 100644 src/ua-client-hints/test/index.js delete mode 100644 src/ua-client-hints/ua-client-hints.d.ts delete mode 100644 src/ua-client-hints/ua-client-hints.js delete mode 100644 src/ua-client-hints/ua-client-hints.mjs delete mode 100644 src/user-agent-helpers/package.json delete mode 100644 src/user-agent-helpers/readme.md delete mode 100644 src/user-agent-helpers/test/index.js delete mode 100644 src/user-agent-helpers/user-agent-helpers.d.ts delete mode 100644 src/user-agent-helpers/user-agent-helpers.js delete mode 100644 src/user-agent-helpers/user-agent-helpers.mjs diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 96aa29a4d..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "env": { - "browser": true, - "commonjs": true, - "es2021": true, - "node": true - }, - "parserOptions": { - "ecmaVersion": "latest" - }, - "rules": { - } -} diff --git a/package.json b/package.json index 1bff1d7f1..951a7f01d 100644 --- a/package.json +++ b/package.json @@ -182,7 +182,6 @@ "@babel/traverse": "7.15.4", "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", - "eslint": "^8.48.0", "jshint": "~2.13.6", "mocha": "~8.2.0", "requirejs": "2.3.2", @@ -219,10 +218,5 @@ "type": "github", "url": "https://github.com/sponsors/faisalman" } - ], - "workspaces": [ - "src/gpu-detect", - "src/ua-client-hints", - "src/user-agent-helpers" ] } diff --git a/script/build-module.js b/script/build-module.js index 2c0c4587d..f8079233c 100755 --- a/script/build-module.js +++ b/script/build-module.js @@ -46,24 +46,6 @@ const modules = [ dest : 'src/extensions/ua-parser-extensions.mjs', title : 'ua-parser-js/extensions', replacements : [] - }, - { - src : 'src/gpu-detect/gpu-detect.js', - dest : 'src/gpu-detect/gpu-detect.mjs', - title : '@ua-parser-js/gpu-detect', - replacements : [] - }, - { - src : 'src/user-agent-helpers/user-agent-helpers.js', - dest : 'src/user-agent-helpers/user-agent-helpers.mjs', - title : '@ua-parser-js/user-agent-helpers', - replacements : [] - }, - { - src : 'src/ua-client-hints/ua-client-hints.js', - dest : 'src/ua-client-hints/ua-client-hints.mjs', - title : '@ua-parser-js/ua-client-hints', - replacements : [] } ]; diff --git a/script/test-all.sh b/script/test-all.sh index c8b2ca29b..37cbd19bf 100755 --- a/script/test-all.sh +++ b/script/test-all.sh @@ -9,7 +9,7 @@ echo ' - lint js code ' npm run test:jshint || exit 1 -npm run test:eslint || exit 1 +#npm run test:eslint || exit 1 echo ' - test using mocha diff --git a/src/gpu-detect/gpu-detect.d.ts b/src/gpu-detect/gpu-detect.d.ts deleted file mode 100644 index bccc81b6c..000000000 --- a/src/gpu-detect/gpu-detect.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class GPUDetect { - static getGPU: { vendor: string, model: string } -} \ No newline at end of file diff --git a/src/gpu-detect/gpu-detect.js b/src/gpu-detect/gpu-detect.js deleted file mode 100644 index 326dbcf43..000000000 --- a/src/gpu-detect/gpu-detect.js +++ /dev/null @@ -1,116 +0,0 @@ -////////////////////////////////////////////// -/* Extracts GPU information from user-agent - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -///////////////////////////////////////////// - -/*jshint esversion: 11 */ - -const rendererMap = [ - [[ - /(intel).*\b(hd\sgraphics\s\d{4}|iris(?:\spro)|gma\s\w+)/i, // Intel - /(nvidia)\s(geforce\s(?:gtx?\s)\d\w+|quadro)/i, // NVIDIA - /\b(sis)\s(\w+)/i // SiS - ], ['vendor', 'model']], - - [[ - /\b(radeon[\shdr\d]+\w{4,5})/i // ATI/AMD - ], ['model', ['vendor', 'AMD']]], - - [[ - /(adreno\s(?:\(tm\)\s)\w+)/i // Qualcomm - ], [['model', /\(tm\)\s/i, ''], ['vendor', 'Qualcomm']]] -]; - -const vendorMap = [ - [[ - /\b(amd|apple|arm|ati|img|intel|nvidia|qualcomm|samsung|sis)\b/i - ], ['vendor']] -]; - -class RegexMap { - - static parse(str, mapper) { - let res = {}; - if (typeof str === 'string') { - for (const [regs, props] of mapper) { - if (!Array.isArray(regs)) { - throw new Error('RegexMap: Expect Array of RegExp'); - } - if (!Array.isArray(props)) { - throw new Error('RegexMap: Expect Array for Properties Mapping'); - } - for (const reg of regs) { - if (!reg instanceof RegExp) { - throw new Error('RegexMap: Expect RegExp Instance'); - } - const matches = reg.exec(str); - if (matches) { - props.forEach((prop, idx) => { - const val = matches[idx+1]; - if (Array.isArray(prop)) { - const key = prop[0]; - if (typeof key !== 'string') { - throw new Error('RegexMap: Expect String Input'); - } - if (prop.length == 2) { - if (typeof prop[1] === 'string') { - res[key] = prop[1]; - } else if (typeof prop[1] === 'function') { - res[key] = prop[1].call(res, val); - } - } else if (prop.length == 3) { - if (prop[1] instanceof RegExp) { - res[key] = val.replace(prop[1], prop[2]); - } else if (typeof prop[1] === 'function') { - res[key] = prop[1].call(res, val, prop[2]); - } - } else if (prop.length == 4) { - res[key] = prop[3].call(res, val.replace(prop[1], prop[2])); - } else { - res[key] = val; - } - } else if (typeof prop === 'string') { - res[prop] = val; - } - }); - if (res) return res; - } - } - } - } - return res; - }; -} - -class GPUDetect { - - static getGPU (strRenderer, strVendor) { - - let gpuInfo = { vendor : undefined, model : undefined }; - - if (typeof strRenderer !== 'string') { - if (globalThis.document) { - const canvas = document.createElement('canvas'); - const gl = canvas.getContext('webgl2') || - canvas.getContext('webgl') || - canvas.getContext('experimental-webgl'); - if (gl) { - const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); - strVendor = gl.getParameter(debugInfo?.UNMASKED_VENDOR_WEBGL); - strRenderer = gl.getParameter(debugInfo?.UNMASKED_RENDERER_WEBGL); - } - } - } - if (strRenderer || strVendor) { - ({ vendor : gpuInfo.vendor, model : gpuInfo.model } = RegexMap.parse(strRenderer, rendererMap)); - gpuInfo.vendor = gpuInfo.vendor ?? RegexMap.parse(strVendor, rendererMap)?.vendor ?? RegexMap.parse(strVendor, vendorMap)?.vendor; - } - return gpuInfo; - } -} - -module.exports = { - GPUDetect -} \ No newline at end of file diff --git a/src/gpu-detect/package.json b/src/gpu-detect/package.json deleted file mode 100644 index c3ffaf9f5..000000000 --- a/src/gpu-detect/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "title": "User-Agent GPU Info", - "name": "@ua-parser-js/gpu-detect", - "version": "0.0.1", - "author": "Faisal Salman ", - "description": "Extracts GPU information from user-agent", - "main": "gpu-detect.js", - "module": "gpu-detect.mjs", - "scripts": { - "test": "mocha ../../test/mocha-test-helpers" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/faisalman/ua-parser-js.git" - }, - "keywords": [ - "ua-parser-js", - "gpu-detection" - ], - "license": "MIT", - "bugs": { - "url": "https://github.com/faisalman/ua-parser-js/issues" - }, - "homepage": "https://github.com/faisalman/ua-parser-js#readme" -} diff --git a/src/gpu-detect/readme.md b/src/gpu-detect/readme.md deleted file mode 100644 index 86b9fa45f..000000000 --- a/src/gpu-detect/readme.md +++ /dev/null @@ -1,16 +0,0 @@ -# @ua-parser-js/gpu-detect - -This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that extracts GPU information from user-agent. - -```sh -npm i @ua-parser-js/gpu-detect -``` - -## Code Example - -// in browser environment -const { vendor, model } = GPUDetect.getGPU(); - -// in non-browser environment -const { vendor, model } = GPUDetect.getGPU("AMD Radeon"); -``` \ No newline at end of file diff --git a/src/gpu-detect/test/index.js b/src/gpu-detect/test/index.js deleted file mode 100644 index 76b7e7046..000000000 --- a/src/gpu-detect/test/index.js +++ /dev/null @@ -1,4 +0,0 @@ -const { GPUDetect } = require('../gpu-detect.js'); - -console.log(GPUDetect.getGPU('AMD Radeon R9 M295X OpenGL Engine')); -console.log(GPUDetect.getGPU('','ATI Technologies Inc.')); \ No newline at end of file diff --git a/src/ua-client-hints/package.json b/src/ua-client-hints/package.json deleted file mode 100644 index 9fdb8eaf7..000000000 --- a/src/ua-client-hints/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "title": "User-Agent Client Hints", - "name": "@ua-parser-js/ua-client-hints", - "version": "0.0.1", - "author": "Faisal Salman ", - "description": "A collection of utility methods for working with user-agent client hints", - "main": "ua-client-hints.js", - "module": "ua-client-hints.mjs", - "scripts": { - "test": "mocha ./test/*" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/faisalman/ua-parser-js.git" - }, - "keywords": [ - "ua-parser-js", - "browser-detection", - "device-detection", - "os-detection", - "user-agent", - "client-hints" - ], - "license": "MIT", - "bugs": { - "url": "https://github.com/faisalman/ua-parser-js/issues" - }, - "homepage": "https://github.com/faisalman/ua-parser-js#readme" -} diff --git a/src/ua-client-hints/readme.md b/src/ua-client-hints/readme.md deleted file mode 100644 index 253f4ebe7..000000000 --- a/src/ua-client-hints/readme.md +++ /dev/null @@ -1,128 +0,0 @@ -# @ua-parser-js/ua-client-hints - -This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client hints. - -```sh -npm i @ua-parser-js/ua-client-hints -``` - -### * `getUAData([props:array]):object` - -Get user-agent client hints values of current instance in form of JS object representation - -### * `setUAData([uaData:object]):UAClientHints` - -Set values of user-agent client hints for the current instance either from navigator.userAgentData or from HTTP headers (Sec-CH-UA-*) - -### * `getSerializedUAData([props:array]):object` - -Get user-agent client hints values of current instance in form of HTTP headers string representation (Sec-CH-UA-*) - -## Code Example - -```js -import { UAClientHints } from '@ua-parser-js/ua-client-hints'; - -/* - Suppose we're in a server having this client hints data: - - const httpHeaders = { - 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', - 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : 'arm', - 'sec-ch-ua-bitness' : '64', - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : 'Pixel 99', - 'sec-ch-ua-platform' : 'Linux', - 'sec-ch-ua-platform-version' : '13' - }; -*/ - -const uaCH = new UAClientHints(); -uaCH.setUAData(httpHeaders); -const uaCHData1 = uaCH.getUAData(); -const uaCHData2 = uaCH.getUAData(['architecture', 'bitness']); - -console.log(uaCHData1); -/* - { - "architecture": "arm", - "bitness": "64", - "brands": [ - { - "brand": "Chromium", - "version": "93" - }, - { - "brand": "Google Chrome", - "version": "93" - }, - { - "brand": " Not;A Brand", - "version": "99" - } - ], - "fullVersionList": [ - { - "brand": "Chromium", - "version": "93.0.1.2" - }, - { - "brand": "Google Chrome", - "version": "93.0.1.2" - }, - { - "brand": " Not;A Brand", - "version": "99.0.1.2" - } - ], - "mobile": true, - "model": "Pixel 99", - "platform": "Linux", - "platformVersion": "13", - "wow64": null, - "formFactor": null - } -*/ - -console.log(uaCHData2); -/* - { - "architecture": "arm", - "bitness": "64" - } -*/ - -uaCH.setUAData({ - "wow64" : true, - "formFactor" : "Automotive" -}); - -const headersData1 = uaCH.getSerializedUAData(); -const headersData2 = uaCH.getSerializedUAData(['brand', 'mobile', 'model']); - -console.log(headersData1); -/* - { - 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', - 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : 'arm', - 'sec-ch-ua-bitness' : '64', - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : 'Pixel 99', - 'sec-ch-ua-platform' : 'Linux', - 'sec-ch-ua-platform-version' : '13', - 'sec-ch-ua-wow64' : '?1', - 'sec-ch-ua-form-factor' : 'Automotive' - }; -*/ - -console.log(headersData2); -/* - { - 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : 'Pixel 99' - }; -*/ -``` \ No newline at end of file diff --git a/src/ua-client-hints/test/index.js b/src/ua-client-hints/test/index.js deleted file mode 100644 index 23d126245..000000000 --- a/src/ua-client-hints/test/index.js +++ /dev/null @@ -1,64 +0,0 @@ -const { UAClientHints } = require('../ua-client-hints'); -const assert = require('assert'); - -describe('Parse CH Headers', () => { - it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => { - - const req = { - headers : { - 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', - 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : '"arm"', - 'sec-ch-ua-bitness' : '"64"', - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : '"Pixel 99"', - 'sec-ch-ua-platform' : '"Linux"', - 'sec-ch-ua-platform-version' : '"13"', - 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' - }}; - - assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(['architecture', 'bitness']), { - "architecture": "arm", - "bitness": "64" - }); - - assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(), { - "architecture": "arm", - "bitness": "64", - "brands": [ - { - "brand": "Chromium", - "version": "93" - }, - { - "brand": "Google Chrome", - "version": "93" - }, - { - "brand": "Not;A Brand", - "version": "99" - } - ], - "fullVersionList": [ - { - "brand": "Chromium", - "version": "93.0.1.2" - }, - { - "brand": "Google Chrome", - "version": "93.0.1.2" - }, - { - "brand": "Not;A Brand", - "version": "99.0.1.2" - } - ], - "formFactor": null, - "mobile": true, - "model": "Pixel 99", - "platform": "Linux", - "platformVersion": "13", - "wow64": null - }); - }); -}); \ No newline at end of file diff --git a/src/ua-client-hints/ua-client-hints.d.ts b/src/ua-client-hints/ua-client-hints.d.ts deleted file mode 100644 index 307765848..000000000 --- a/src/ua-client-hints/ua-client-hints.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -export type UABrowser = { - brand: string | null, - version: string | null -}; - -export type UADataType = boolean | string | Array | null; -export type UADataField = - 'brands' | - 'mobile' | - 'platform' | - 'architecture' | - 'bitness' | - 'formFactor' | - 'fullVersionList' | - 'model' | - 'platformVersion' | - 'wow64'; - -export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list'; -export type HeaderField = - 'sec-ch-ua-arch' | - 'sec-ch-ua-bitness' | - 'sec-ch-ua' | - 'sec-ch-ua-form-factor' | - 'sec-ch-ua-full-version-list' | - 'sec-ch-ua-mobile' | - 'sec-ch-ua-model' | - 'sec-ch-ua-platform' | - 'sec-ch-ua-platform-version' | - 'sec-ch-ua-wow64'; - -export class UAClientHints { - #ch: Map; - #parseHeader(str: string, type: HeaderType): UADataType; - #serialize(data: UADataType, type: HeaderType): string; - getSerializedUAData(): Record; - getUAData(props?: Array): Record; - setUAData(uaData: Record | Record): UAClientHints; -}; \ No newline at end of file diff --git a/src/ua-client-hints/ua-client-hints.js b/src/ua-client-hints/ua-client-hints.js deleted file mode 100644 index d4ddc6077..000000000 --- a/src/ua-client-hints/ua-client-hints.js +++ /dev/null @@ -1,130 +0,0 @@ -////////////////////////////////////////////// -/* A collection of utility methods for - working with user-agent client hints - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -///////////////////////////////////////////// - -/*jshint esversion: 11 */ - -const fieldType = Object.freeze({ - Boolean : 'sf-boolean', - List : 'sf-list', - String : 'sf-string' -}); - -const uaCHMap = Object.freeze({ - architecture : { - field : 'Sec-CH-UA-Arch', - type : fieldType.String - }, - bitness : { - field : 'Sec-CH-UA-Bitness', - type : fieldType.String - }, - brands : { - field : 'Sec-CH-UA', - type : fieldType.List - }, - formFactor : { - field : 'Sec-CH-UA-Form-Factor', - type : fieldType.String - }, - fullVersionList : { - field : 'Sec-CH-UA-Full-Version-List', - type : fieldType.List - }, - mobile : { - field : 'Sec-CH-UA-Mobile', - type : fieldType.Boolean - }, - model : { - field : 'Sec-CH-UA-Model', - type : fieldType.String - }, - platform : { - field : 'Sec-CH-UA-Platform', - type : fieldType.String - }, - platformVersion : { - field : 'Sec-CH-UA-Platform-Version', - type : fieldType.String - }, - wow64 : { - field : 'Sec-CH-UA-WOW64', - type : fieldType.Boolean - } -}); - -class UAClientHints { - - #uach = new Map(); - - constructor () { - for (const key in uaCHMap) { - this.#uach.set(key, null); - } - return this; - }; - - #parseHeader (str, type) { - if (!str) { - return null; - } - switch (type) { - case fieldType.Boolean: - return /\?1/.test(str); - case fieldType.List: - return str.replace(/\\?\"/g, '') - .split(',') - .map(brands => { - const [brand, version] = brands.trim().split(';v='); - return { - brand : brand, - version : version - }; - }); - case fieldType.String: - return str.replace(/\s*\\?\"\s*/g, ''); - default: - return ''; - } - }; - - #serialize(data, type) { - throw new Error('Not implemented yet'); - //return ''; - } - - getSerializedUAData() { - throw new Error('Not implemented yet'); - //let http = {}; - //return http; - } - - getUAData(props) { - if (props) { - return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)])); - } - return Object.fromEntries(this.#uach); - } - - setUAData(uaDataValues) { - if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) { - for (const val in uaCHMap) { - const { field, type } = uaCHMap[val]; - this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type)); - } - } else { - for (const value in uaDataValues) { - if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]); - } - } - return this; - }; -} - -module.exports = { - UAClientHints -}; \ No newline at end of file diff --git a/src/ua-client-hints/ua-client-hints.mjs b/src/ua-client-hints/ua-client-hints.mjs deleted file mode 100644 index 047f5c828..000000000 --- a/src/ua-client-hints/ua-client-hints.mjs +++ /dev/null @@ -1,134 +0,0 @@ -// Generated ESM version of @ua-parser-js/ua-client-hints -// DO NOT EDIT THIS FILE! -// Source: /src/ua-client-hints/ua-client-hints.js - -////////////////////////////////////////////// -/* A collection of utility methods for - working with user-agent client hints - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -///////////////////////////////////////////// - -/*jshint esversion: 11 */ - -const fieldType = Object.freeze({ - Boolean : 'sf-boolean', - List : 'sf-list', - String : 'sf-string' -}); - -const uaCHMap = Object.freeze({ - architecture : { - field : 'Sec-CH-UA-Arch', - type : fieldType.String - }, - bitness : { - field : 'Sec-CH-UA-Bitness', - type : fieldType.String - }, - brands : { - field : 'Sec-CH-UA', - type : fieldType.List - }, - formFactor : { - field : 'Sec-CH-UA-Form-Factor', - type : fieldType.String - }, - fullVersionList : { - field : 'Sec-CH-UA-Full-Version-List', - type : fieldType.List - }, - mobile : { - field : 'Sec-CH-UA-Mobile', - type : fieldType.Boolean - }, - model : { - field : 'Sec-CH-UA-Model', - type : fieldType.String - }, - platform : { - field : 'Sec-CH-UA-Platform', - type : fieldType.String - }, - platformVersion : { - field : 'Sec-CH-UA-Platform-Version', - type : fieldType.String - }, - wow64 : { - field : 'Sec-CH-UA-WOW64', - type : fieldType.Boolean - } -}); - -class UAClientHints { - - #uach = new Map(); - - constructor () { - for (const key in uaCHMap) { - this.#uach.set(key, null); - } - return this; - }; - - #parseHeader (str, type) { - if (!str) { - return null; - } - switch (type) { - case fieldType.Boolean: - return /\?1/.test(str); - case fieldType.List: - return str.replace(/\\?\"/g, '') - .split(',') - .map(brands => { - const [brand, version] = brands.trim().split(';v='); - return { - brand : brand, - version : version - }; - }); - case fieldType.String: - return str.replace(/\s*\\?\"\s*/g, ''); - default: - return ''; - } - }; - - #serialize(data, type) { - throw new Error('Not implemented yet'); - //return ''; - } - - getSerializedUAData() { - throw new Error('Not implemented yet'); - //let http = {}; - //return http; - } - - getUAData(props) { - if (props) { - return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)])); - } - return Object.fromEntries(this.#uach); - } - - setUAData(uaDataValues) { - if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) { - for (const val in uaCHMap) { - const { field, type } = uaCHMap[val]; - this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type)); - } - } else { - for (const value in uaDataValues) { - if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]); - } - } - return this; - }; -} - -export { - UAClientHints -}; \ No newline at end of file diff --git a/src/user-agent-helpers/package.json b/src/user-agent-helpers/package.json deleted file mode 100644 index ba573084f..000000000 --- a/src/user-agent-helpers/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "title": "User-Agent Helpers", - "name": "@ua-parser-js/user-agent-helpers", - "version": "0.0.2", - "author": "Faisal Salman ", - "description": "A collection of utility methods for working with user-agent", - "main": "user-agent-helpers.js", - "module": "user-agent-helpers.mjs", - "scripts": { - "test": "mocha ./test/*" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/faisalman/ua-parser-js.git" - }, - "keywords": [ - "ua-parser-js", - "browser-detection", - "device-detection", - "os-detection", - "user-agent", - "client-hints" - ], - "license": "MIT", - "bugs": { - "url": "https://github.com/faisalman/ua-parser-js/issues" - }, - "homepage": "https://github.com/faisalman/ua-parser-js#readme", - "dependencies": { - "@ua-parser-js/client-hints-helpers": "*" - } -} diff --git a/src/user-agent-helpers/readme.md b/src/user-agent-helpers/readme.md deleted file mode 100644 index 472070765..000000000 --- a/src/user-agent-helpers/readme.md +++ /dev/null @@ -1,77 +0,0 @@ -# @ua-parser-js/user-agent-helpers - -This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent. - -```sh -npm i @ua-parser-js/user-agent-helpers -``` - -### * `isFrozenUA(ua:string):boolean` - -Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/) - -### * `unfreezeUA([ua:string,ch:object]|[headers:object]):Promise` - -Construct new unfreezed user-agent string using real data from client hints - -## Code Example - -```js -import { isFrozenUA } from '@ua-parser-js/user-agent-helpers'; - -const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36"; -const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36"; - -console.log(isFrozenUA(regularMobileUA)); -// false - -console.log(isFrozenUA(freezedMobileUA)); -// true -``` - -```js -import { unfreezeUA } from '@ua-parser-js/user-agent-helpers'; - -/* - Suppose we're in a browser having this client hints data: - - { - fullVersionList: [ - { - brand: 'New Browser', - version: '110.1.2.3' - }, - { - brand: 'Chromium', - version: '110.1.2.3' - }, - { - brand: 'Not(A:Brand', - version: '110' - } - ], - platform: 'Windows', - platformVersion: '13.0.0', - architecture: 'arm' - } - - With a frozen user-agent: - - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36' -*/ - -// Now let's generate a complete user-agent: -unfreezeUA() - .then(newUA => console.log(newUA)); -// 'Mozilla/5.0 (Windows NT 11.0; ARM) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36' - -/* -// Alternatively: -const ua = navigator.userAgent; -const ch = await navigator.userAgentData.getHighEntropyValues(); -const newUA = await unfreezeUA(ua, ch); - -// Server environment: -const newUA = await unfreezeUA(req.headers); -*/ -``` \ No newline at end of file diff --git a/src/user-agent-helpers/test/index.js b/src/user-agent-helpers/test/index.js deleted file mode 100644 index 4cea95409..000000000 --- a/src/user-agent-helpers/test/index.js +++ /dev/null @@ -1,56 +0,0 @@ -const { isFrozenUA, unfreezeUA } = require('../user-agent-helpers'); -const { UAClientHints } = require('@ua-parser-js/ua-client-hints'); -const assert = require('assert'); - -describe('isFrozenUA()', () => { - it('Returns whether a user agent is frozen', () => { - - const regularWindowsUA = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36"; - const freezedWindowsUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36"; - - const regularMacUA = ""; - const freezedMacUA = ""; - - const regularLinuxUA = ""; - const freezedLinuxUA = ""; - - const regularCrOSUA = ""; - const freezedCrOSUA = ""; - - const regularFuchsiaUA = ""; - const freezedFuchsiaUA = ""; - - const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36"; - const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36"; - - const regularTabletUA = "Mozilla/5.0 (Linux; Android 9; SM-T810) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36"; - const freezedTabletUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36"; - - assert.strictEqual(isFrozenUA(regularWindowsUA), false); - assert.strictEqual(isFrozenUA(freezedWindowsUA), true); - assert.strictEqual(isFrozenUA(regularMobileUA), false); - assert.strictEqual(isFrozenUA(freezedMobileUA), true); - assert.strictEqual(isFrozenUA(regularTabletUA), false); - assert.strictEqual(isFrozenUA(freezedTabletUA), true); - }); -}); - -const req = { - headers : { - 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', - 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : '"arm"', - 'sec-ch-ua-bitness' : '"64"', - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : '"Pixel 99"', - 'sec-ch-ua-platform' : '"Linux"', - 'sec-ch-ua-platform-version' : '"13"', - 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' -}}; - -describe('unfreezeUA()', () => { - it('returns an unfreezed user-agent using real data from client hints HTTP headers (sec-ch-ua)', async () => { - const unfreezed = await unfreezeUA(req.headers); - assert.strictEqual(unfreezed, 'Mozilla/5.0 (X11; Linux arm64) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/93.0.1.2 Chrome/93.0.1.2 Safari/537.36'); - }); -}); \ No newline at end of file diff --git a/src/user-agent-helpers/user-agent-helpers.d.ts b/src/user-agent-helpers/user-agent-helpers.d.ts deleted file mode 100644 index d77af20c8..000000000 --- a/src/user-agent-helpers/user-agent-helpers.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -export type UABrowser = { - brand: string | null, - version: string | null -}; - -export type UADataType = boolean | string | Array | null; -export type UADataField = - 'brands' | - 'mobile' | - 'platform' | - 'architecture' | - 'bitness' | - 'formFactor' | - 'fullVersionList' | - 'model' | - 'platformVersion' | - 'wow64'; - -export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list'; -export type HeaderField = - 'sec-ch-ua-arch' | - 'sec-ch-ua-bitness' | - 'sec-ch-ua' | - 'sec-ch-ua-form-factor' | - 'sec-ch-ua-full-version-list' | - 'sec-ch-ua-mobile' | - 'sec-ch-ua-model' | - 'sec-ch-ua-platform' | - 'sec-ch-ua-platform-version' | - 'sec-ch-ua-wow64'; - -export function isFrozenUA(ua: string): boolean; -export function unfreezeUA(): Promise; -export function unfreezeUA(ua: string, ch: Record): Promise; -export function unfreezeUA(headers: Record): Promise; \ No newline at end of file diff --git a/src/user-agent-helpers/user-agent-helpers.js b/src/user-agent-helpers/user-agent-helpers.js deleted file mode 100644 index cfc9ecf07..000000000 --- a/src/user-agent-helpers/user-agent-helpers.js +++ /dev/null @@ -1,97 +0,0 @@ -//////////////////////////////////////////////////// -/* A collection of utility methods for user-agent - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -/////////////////////////////////////////////////// - -/*jshint esversion: 11 */ - -const { UAClientHints } = require('@ua-parser-js/ua-client-hints'); - -/* - # Reference: - https://www.chromium.org/updates/ua-reduction/ - - # Desktop - --- - Format: - Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 - - Possible values: - - Windows NT 10.0; Win64; x64 - - Macintosh; Intel Mac OS X 10_15_7 - - X11; Linux x86_64 - - X11; CrOS x86_64 14541.0.0 - - Fuchsia - - # Mobile & Tablet: (except iOS/Android WebView) - --- - Format: - Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 - - Possible values: - - "Mobile" - - "" (empty string for Tablets & Desktop) -*/ - -const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua); - -const unfreezeUA = async (ua, ch) => { - const env = typeof navigator == 'undefined' ? 'node' : 'browser'; - if (env == 'node') { - if (!ua['user-agent']) { - throw new Error('User-Agent header not found'); - } - ch = new UAClientHints().setUAData(ua).getUAData(); - ua = ua['user-agent']; - } else { - ua = ua || navigator.userAgent; - ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']); - } - if (isFrozenUA(ua) && ch) { - switch (ch.platform) { - case 'Windows': - let [major, minor] = ch.platformVersion - .split('.') - .map(num => parseInt(num, 10)); - major = (major < 1) ? '6' : (major >= 13) ? '11' : '10'; - ua = ua .replace(/(?Windows NT) 10\.0/, `$ ${major}.${minor}`) - .replace(/; (?Win64; x64)/, - (ch.architecture == 'arm') ? - '; ARM' : - (ch.wow64) ? - '; WOW64' : - (ch.architecture == 'x86' && ch.bitness != '64') ? - '' : '; $'); - break; - case 'Android': - ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`); - break; - case 'Linux': - case 'Chrome OS': - ua = ua.replace(/(?x86_64)/, - (ch.architecture == 'arm') ? - ((ch.bitness == '64') ? 'arm64' : 'arm') : - (ch.architecture == 'x86' && ch.bitness != '64') ? - 'x86' : '$'); - break; - case 'macOS': - ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`); - break; - } - if (ch.fullVersionList) { - ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /, - ch.fullVersionList - .filter(browser => !/not.a.brand/i.test(browser.brand)) - .map(browser => `${browser.brand.replace(/^google /i,'')}/${browser.version} `) - .join('')); - } - } - return ua; -}; - -module.exports = { - isFrozenUA, - unfreezeUA -}; \ No newline at end of file diff --git a/src/user-agent-helpers/user-agent-helpers.mjs b/src/user-agent-helpers/user-agent-helpers.mjs deleted file mode 100644 index b8f398e09..000000000 --- a/src/user-agent-helpers/user-agent-helpers.mjs +++ /dev/null @@ -1,101 +0,0 @@ -// Generated ESM version of @ua-parser-js/user-agent-helpers -// DO NOT EDIT THIS FILE! -// Source: /src/user-agent-helpers/user-agent-helpers.js - -//////////////////////////////////////////////////// -/* A collection of utility methods for user-agent - https://github.com/faisalman/ua-parser-js - Author: Faisal Salman - MIT License */ -/////////////////////////////////////////////////// - -/*jshint esversion: 11 */ - -import { UAClientHints } from '@ua-parser-js/ua-client-hints'; - -/* - # Reference: - https://www.chromium.org/updates/ua-reduction/ - - # Desktop - --- - Format: - Mozilla/5.0 () AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 - - Possible values: - - Windows NT 10.0; Win64; x64 - - Macintosh; Intel Mac OS X 10_15_7 - - X11; Linux x86_64 - - X11; CrOS x86_64 14541.0.0 - - Fuchsia - - # Mobile & Tablet: (except iOS/Android WebView) - --- - Format: - Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/.0.0.0 Safari/537.36 - - Possible values: - - "Mobile" - - "" (empty string for Tablets & Desktop) -*/ - -const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua); - -const unfreezeUA = async (ua, ch) => { - const env = typeof navigator == 'undefined' ? 'node' : 'browser'; - if (env == 'node') { - if (!ua['user-agent']) { - throw new Error('User-Agent header not found'); - } - ch = new UAClientHints().setUAData(ua).getUAData(); - ua = ua['user-agent']; - } else { - ua = ua || navigator.userAgent; - ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']); - } - if (isFrozenUA(ua) && ch) { - switch (ch.platform) { - case 'Windows': - let [major, minor] = ch.platformVersion - .split('.') - .map(num => parseInt(num, 10)); - major = (major < 1) ? '6' : (major >= 13) ? '11' : '10'; - ua = ua .replace(/(?Windows NT) 10\.0/, `$ ${major}.${minor}`) - .replace(/; (?Win64; x64)/, - (ch.architecture == 'arm') ? - '; ARM' : - (ch.wow64) ? - '; WOW64' : - (ch.architecture == 'x86' && ch.bitness != '64') ? - '' : '; $'); - break; - case 'Android': - ua = ua.replace(/(?Android) 10; K/, `$ ${ch.platformVersion}; ${ch.model}`); - break; - case 'Linux': - case 'Chrome OS': - ua = ua.replace(/(?x86_64)/, - (ch.architecture == 'arm') ? - ((ch.bitness == '64') ? 'arm64' : 'arm') : - (ch.architecture == 'x86' && ch.bitness != '64') ? - 'x86' : '$'); - break; - case 'macOS': - ua = ua.replace(/(?Mac OS X) 10_15_7/, `$ ${ch.platformVersion.replace(/\./, '_')}`); - break; - } - if (ch.fullVersionList) { - ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /, - ch.fullVersionList - .filter(browser => !/not.a.brand/i.test(browser.brand)) - .map(browser => `${browser.brand.replace(/^google /i,'')}/${browser.version} `) - .join('')); - } - } - return ua; -}; - -export { - isFrozenUA, - unfreezeUA -}; \ No newline at end of file From bf1d7267f632679aca8c68ce8e7f5daadf8a3796 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Sep 2023 17:56:50 +0700 Subject: [PATCH 127/388] Update contributors --- package.json | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 951a7f01d..37315a465 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,11 @@ "Carl C Von Lewin ", "CESAR RAMOS ", "Chad Killingsworth ", + "chenhui9279 ", + "chenyuan-new <53860479+chenyuan-new@users.noreply.github.com>", "Christopher De Cairos ", "Cyrille David ", + "Dante ", "Dario Vladovic ", "David Annez ", "Davit Barbakadze ", @@ -63,10 +66,12 @@ "Faisal Salman ", "Frédéric Camblor ", "Frederik Ring ", + "Garrit Franke ", "Gerald Host ", "Germán M. Bravo ", "Grigory Dmitrenko ", "gulpin ", + "Hans Ott ", "Hendrik Helwich ", "Hermann Ebert ", "hr6r ", @@ -74,6 +79,7 @@ "Ildar Kamalov ", "insanehong ", "jackpoll ", + "Jacky Choo ", "Jake Mc ", "JBYoshi <12983479+JBYoshi@users.noreply.github.com>", "Joey Parrish ", @@ -83,11 +89,14 @@ "Josh Goldberg ", "Junki-Ishida ", "Kendall Buchanan ", + "KnifeLemon ", + "kNoAPP ", "Lee Treveil ", "leonardo ", "Levente Balogh ", "Liam Quinn ", "Lithin ", + "liujunlve ", "ll-syber <670159357@qq.com>", "Loris Guignard ", "Lukas Drgon ", @@ -102,6 +111,8 @@ "Max Nordlund ", "Michael Hess ", "MimyyK ", + "Mok ", + "nabetama ", "naoh ", "Nicholas Ionata ", "Nikhil Motiani ", @@ -110,7 +121,9 @@ "niris ", "Nobuo Okada ", "o.drapeza ", + "Oscar Becerra ", "otakuSiD ", + "Paris Morgan ", "patrick-nurt ", "Pavel Studeny ", "Peter Dave Hello ", @@ -120,19 +133,24 @@ "Queen Vinyl Darkscratch ", "Raine Makelainen ", "Raman Savaryn ", + "Riley Shaw ", "Robert Tod ", "roman.savarin ", "Ron Korland ", "Ross Noble ", "ruicong <466403866@qq.com>", + "Runar Heggset ", + "Ryohei Shima ", "Sandro Sonntag ", "sgautrea ", + "shaharmor ", "Shane Gautreau ", "Shane Thacker ", "Shreedhar ", "Simon Eisenmann ", "Simon Lang ", "Stiekel ", + "sunny-mwx <30586210+sunny-mwx@users.noreply.github.com>", "sUP ", "Sylvain Gizard ", "szchenghuang ", @@ -140,10 +158,13 @@ "Tony Tomarchio ", "Ulrich Schmidt ", "Vadim Kurachevsky ", + "Varun Sharma ", + "XhmikosR ", "Yılmaz ", "yuanyang ", "Yun Young-jin ", - "Zach Bjornson " + "Zach Bjornson ", + "Ziding Zhang " ], "type": "commonjs", "main": "src/main/ua-parser.js", From 9652169da03330d54c0bd43d0e3ff41e45850725 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Sep 2023 20:55:02 +0700 Subject: [PATCH 128/388] Update changelog --- changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog.md b/changelog.md index 5339d6cb7..0b118335e 100644 --- a/changelog.md +++ b/changelog.md @@ -33,6 +33,12 @@ Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. +## Version 0.7.36 / 1.0.36 +- Add new browser: Snapchat +- Add new devices: Infinix, Tecno +- Improve device detection: Amazon Fire TV, Xiaomi POCO +- Improve OS detection: iOS + ## Version 0.7.35 / 1.0.35 - Fix result from user-supplied user-agent being altered - Add new browser: Heytap, TikTok From f17d2d7664c5abacca6a4d8db5c80e7fdce9eed2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 17 Sep 2023 21:55:14 +0700 Subject: [PATCH 129/388] Add CODE OF CONDUCT --- CODE_OF_CONDUCT.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..b148af6e5 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,29 @@ +# UAParser.js Code of Conduct + +## Introduction + +Welcome to the UAParser.js community! We're here to collaborate on developing an awesome project. Here are some general guidelines to make our community a great place: + +### 1. Be Kind, Honest, and Respectful + +Always treat others with kindness and respect. We value different opinions and encourage positive communication. + +### 2. Keep Conversations Civil and On-Topic + +Please keep discussions related to the project. If you want to talk about something else, find the right place for it. + +### 3. Mutual Assistance, Appreciation, and Acknowledgement + +Feel free to ask for help, show gratitude for contributions, and make sure to give credit where it's due. + +### 4. Resolving Disagreements + +In the event of a disagreement, we encourage open and respectful dialogue. It's important to remember that it's okay to have differing opinions, and if a common ground can't be reached, we suggest using the 'agree to disagree' approach. + +## Reporting Issues + +If you see any behavior that goes against this code of conduct, report it to [f@faisalman.com](mailto:f@faisalman.com). + +## Conclusion + +Together, we can make this project awesome! \ No newline at end of file From 9b182526fafc84c529e8c9b137d6b0f07d1a9820 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Sep 2023 00:07:17 +0700 Subject: [PATCH 130/388] Rename markdown files to uppercase --- changelog.md => CHANGELOG.md | 0 license.md => LICENSE.md | 0 readme.md => README.md | 0 security.md => SECURITY.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename changelog.md => CHANGELOG.md (100%) rename license.md => LICENSE.md (100%) rename readme.md => README.md (100%) rename security.md => SECURITY.md (100%) diff --git a/changelog.md b/CHANGELOG.md similarity index 100% rename from changelog.md rename to CHANGELOG.md diff --git a/license.md b/LICENSE.md similarity index 100% rename from license.md rename to LICENSE.md diff --git a/readme.md b/README.md similarity index 100% rename from readme.md rename to README.md diff --git a/security.md b/SECURITY.md similarity index 100% rename from security.md rename to SECURITY.md From 6ea6936632503d32b67fe826103b1a19da9b26a5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Sep 2023 00:09:27 +0700 Subject: [PATCH 131/388] Improve device detection: Realme --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 89fa07f1c..04ac3c167 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -505,7 +505,7 @@ ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [ // Realme - /\b(rmx[12]\d{3})(?: bui|;|\))/i + /\b(rmx[1-3]\d{3})(?: bui|;|\))/i ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ // Motorola diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 093faa92a..267489dc7 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1637,11 +1637,20 @@ "type": "mobile" } }, + { + "desc": "Realme 8", + "ua": "Mozilla/5.0 (Linux; Android 12; RMX3085) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX3085", + "type": "mobile" + } + }, { "desc": "Realme 9 Pro", "ua": "Mozilla/5.0 (Linux; Android 13; RMX3471) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", "expect": { - "vendor": "undefined", + "vendor": "Realme", "model": "RMX3471", "type": "mobile" } @@ -1650,7 +1659,7 @@ "desc": "Realme GT Master", "ua": "Mozilla/5.0 (Linux; Android 13; RMX3363) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", "expect": { - "vendor": "undefined", + "vendor": "Realme", "model": "RMX3363", "type": "mobile" } From 817c5835efb7767d11acd714453aafd785edfabb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Sep 2023 00:11:46 +0700 Subject: [PATCH 132/388] Add new device vendor: Ulefone https://ulefone.com/ https://www.gsmarena.com/ulefone-phones-124.php --- src/main/ua-parser.js | 4 ++ test/specs/device-all.json | 90 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 04ac3c167..b118ca3ef 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -591,6 +591,10 @@ /droid.+; (m[1-5] note) bui/i, /\bmz-([-\w]{2,})/i ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [ + + // Ulefone + /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i + ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [ // MIXED /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i, diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 267489dc7..264f9128d 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2366,6 +2366,96 @@ "type": "smarttv" } }, + { + "desc": "Ulefone Armor", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Armor Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 6.0; Armor) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 YaBrowser/20.4.2.101.00 SA/1 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 8 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; Armor 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.192 Mobile Safari/537.36 OPR/74.1.3922.71199", + "expect": { + "vendor": "Ulefone", + "model": "Armor 8 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 12 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; Armor 12 5G Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor 12 5G", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 20WT", + "ua": "Mozilla/5.0 (Linux; Android 12; Armor 20WT) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor 20WT", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor Pad", + "ua": "Mozilla/5.0 (Linux; Android 12; Armor Pad Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/431.0.0.30.108;]", + "expect": { + "vendor": "Ulefone", + "model": "Armor Pad", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor X5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; Armor X5 Pro Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/430.0.0.23.113;]", + "expect": { + "vendor": "Ulefone", + "model": "Armor X5 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 14 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor14 Pro Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.138 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor14 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 18T", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor 18T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor 18T", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 19T", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor 19T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.192 Mobile Safari/537.36 OPR/74.3.3922.71982", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor 19T", + "type": "mobile" + } + }, { "desc": "Xiaomi 2013023", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; en-US; 2013023 Build/HM2013023) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/10.0.1.512 U3/0.8.0 Mobile Safari/533.1", From aa76da90d9c9248b49ece250129b617052f40bc0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Sep 2023 00:31:46 +0700 Subject: [PATCH 133/388] Fix #651 - Improve device detection: Xiaomi Redmi --- src/main/ua-parser.js | 2 ++ test/specs/device-all.json | 44 ++++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b118ca3ef..60dcb8101 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -489,8 +489,10 @@ /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi + /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|\))/i, // Xiaomi Redmi 'numeric' models /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 264f9128d..0e96e3673 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2456,6 +2456,42 @@ "type": "mobile" } }, + { + "desc": "Xiaomi 2201117TG", + "ua": "Mozilla/5.0 (Linux; Android 11; 2201117TG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.98 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "2201117TG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi M2004J19C", + "ua": "Mozilla/5.0 (Linux; Android 11; M2004J19C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.77 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2004J19C", + "type": "mobile" + } + }, + { + "desc": "Xiaomi M2006C3MNG", + "ua": "Mozilla/5.0 (Linux; Android 11; M2006C3MNG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2006C3MNG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi 21061119DG", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; 21061119DG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 YaBrowser/23.3.7.24.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "21061119DG", + "type": "mobile" + } + }, { "desc": "Xiaomi 2013023", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; en-US; 2013023 Build/HM2013023) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/10.0.1.512 U3/0.8.0 Mobile Safari/533.1", @@ -2676,7 +2712,7 @@ "desc": "Xiaomi Redmi 10C", "ua": "Mozilla/5.0 (Linux; Android 12; 220333QAG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", "expect": { - "vendor": "undefined", + "vendor": "Xiaomi", "model": "220333QAG", "type": "mobile" } @@ -2730,7 +2766,7 @@ "desc": "XiaoMi Redmi Note 10 5G", "ua": "Mozilla/5.0 (Linux; Android 12; M2103K19C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Mobile Safari/537.36", "expect": { - "vendor": "undefined", + "vendor": "Xiaomi", "model": "M2103K19C", "type": "mobile" } @@ -2739,7 +2775,7 @@ "desc": "XiaoMi Redmi Note 10 Pro", "ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", "expect": { - "vendor": "undefined", + "vendor": "Xiaomi", "model": "M2101K6P", "type": "mobile" } @@ -2748,7 +2784,7 @@ "desc": "XiaoMi Redmi Note 10 Pro", "ua": "Mozilla/5.0 (Linux; Android 12; M2101K6G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", "expect": { - "vendor": "undefined", + "vendor": "Xiaomi", "model": "M2101K6G", "type": "mobile" } From b3f4321bb676723953aed2018e72f864754c91c2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Sep 2023 09:32:30 +0700 Subject: [PATCH 134/388] Modify issue template: bug report should include library version --- .github/ISSUE_TEMPLATE/bug_report.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea782..2ba42577c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,11 @@ assignees: '' --- +**Library version** +Which version of the library that you use, eg: v0.7.35 or v2.0.0-alpha.3 + +For the issue related with detection result, you can use the demo section in https://uaparser.js.org to confirm + **Describe the bug** A clear and concise description of what the bug is. From b5546ee39f5351424b81373f474bd26285b1fc2c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 26 Sep 2023 11:27:13 +0700 Subject: [PATCH 135/388] Breaking change: switch license to AGPLv3 --- CHANGELOG.md | 1 + LICENSE.md | 637 +++++++++++++++++++++++++++++++++++++++++++++++++-- README.md | 35 ++- package.json | 2 +- 4 files changed, 633 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b118335e..e56ee43fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # Version 2.0 - What's breaking: + - Dual-licensed under AGPL v3 or Commercial License - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` - What's new: diff --git a/LICENSE.md b/LICENSE.md index 9ba50e27f..213ad0b2e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +1,616 @@ -MIT License - -Copyright (c) 2012-2023 Faisal Salman <> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +# GNU AFFERO GENERAL PUBLIC LICENSE + +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +## Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains +free software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing +under this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public +License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your +version supports such interaction) an opportunity to receive the +Corresponding Source of your version by providing access to the +Corresponding Source from a network server at no charge, through some +standard or customary means of facilitating copying of software. This +Corresponding Source shall include the Corresponding Source for any +work covered by version 3 of the GNU General Public License that is +incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever +published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/README.md b/README.md index 43d66e94a..402eadcd5 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client-Hints data that can be used either in browser (client-side) or node.js (server-side). * Author : Faisal Salman <> -* Demo : https://faisalman.github.io/ua-parser-js +* Demo : https://uaparser.js.org * Source : https://github.com/faisalman/ua-parser-js * Documentation : * v1 : https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation - * v2 : https://faisalman.github.io/ua-parser-js-docs/v2 + * v2 : https://docs.uaparser.js.org/v2 *** @@ -630,24 +630,19 @@ Made with [contributors-img](https://contrib.rocks). # License -MIT License +AGPL v3 License Copyright (c) 2012-2023 Faisal Salman <> -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see http://www.gnu.org/licenses/. diff --git a/package.json b/package.json index 37315a465..ae0e7f2d1 100644 --- a/package.json +++ b/package.json @@ -213,7 +213,7 @@ "type": "git", "url": "https://github.com/faisalman/ua-parser-js.git" }, - "license": "MIT", + "license": "AGPL-3.0-or-later", "engines": { "node": "*" }, From 91d2d2c0e8caca4ef2f7c5d12a7cc144b3d816c2 Mon Sep 17 00:00:00 2001 From: Hyunbin <47051820+hyunbinseo@users.noreply.github.com> Date: Wed, 27 Sep 2023 10:07:46 +0900 Subject: [PATCH 136/388] fix: changelog link in readme (#672) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 402eadcd5..d24cb538d 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro --- # Version 2.0 -What's new & breaking, please read [CHANGELOG](changelog.md) before upgrading. +What's new & breaking, please read [CHANGELOG](CHANGELOG.md) before upgrading. # Documentation ### `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData` From ea4f145e6428539218af8e777ecdc1f3b0b72f01 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 28 Sep 2023 11:43:43 +0700 Subject: [PATCH 137/388] Add pull_request_template.md --- .github/PULL_REQUEST_TEMPLATE/pull_request_template.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 000000000..2df153f92 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1 @@ +- [ ] I have read and accept the [Contributor License Agreement (CLA)](https://gist.github.com/faisalman/2ed16621ebb544157eba85a7f7381417) Document and I hereby sign the CLA \ No newline at end of file From 954ce3575567ce528df256b6f8a73ef041d72455 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 28 Sep 2023 18:20:49 +0700 Subject: [PATCH 138/388] Add CONTRIBUTING.md - general instruction for contributors --- CONTRIBUTING.md | 7 +++++++ package.json | 1 + 2 files changed, 8 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..f7ac0d654 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# UAParser.js: How to Contribute + +* Fork and clone this repository +* Make some changes as required +* Write unit test to showcase its functionality under `/test` +* Run the test suites to make sure it's not breaking anything `$ npm run build+test` +* Submit a pull request under `develop` branch & check the CLA in the submission form \ No newline at end of file diff --git a/package.json b/package.json index ae0e7f2d1..444f87060 100644 --- a/package.json +++ b/package.json @@ -190,6 +190,7 @@ ], "scripts": { "build": "./script/build-dist.sh && ./script/build-module.js", + "build+test": "npm run build && npm run test", "fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync", "test": "./script/test-all.sh", "test:eslint": "eslint src && eslint script", From a4b4e8a2c2f9a150ba613448222ca2470b37cc43 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 29 Sep 2023 08:37:39 +0700 Subject: [PATCH 139/388] Update issue templates --- .github/ISSUE_TEMPLATE/custom.md | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/custom.md diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md deleted file mode 100644 index 48d5f81fa..000000000 --- a/.github/ISSUE_TEMPLATE/custom.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Custom issue template -about: Describe this issue template's purpose here. -title: '' -labels: '' -assignees: '' - ---- - - From 8fea17f296856d7a4aca2725c622afaf1052d754 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 29 Sep 2023 23:41:00 +0700 Subject: [PATCH 140/388] Update readme & changelog --- CHANGELOG.md | 30 +-- README.md | 612 ++------------------------------------------------- 2 files changed, 23 insertions(+), 619 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e56ee43fd..84bcaf048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,33 +2,19 @@ # Version 2.0 - What's breaking: - - Dual-licensed under AGPL v3 or Commercial License + - Dual-licensed under AGPLv3 or PRO License - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` - What's new: - - Add some new methods in result object: - - Add support for client hints: `withClientHints()` - - Add support for feature detection: `withFeatureCheck()` + - Some new methods in result object: + - Support for client hints: `withClientHints()` + - Support for feature detection: `withFeatureCheck()` - Utility for easy comparison: `is()` - Utility to print full-name: `toString()` - - Add support for ES module `import { UAParser } from 'ua-parser-js'` - - Provide Enums `'ua-parser-js/enums'` - - Provide Extensions `'ua-parser-js/extensions'` - - Provide Helpers `'ua-parser-js/helpers'` - -## Version 2.0.0-alpha.3 -- Add `withFeatureCheck()` method -- Add `isFrozenUA()` method in `helpers` submodule -- Add `MediaPlayers` & `Modules` in `extensions` submodule -- Fix issue with ESM import - -## Version 2.0.0-alpha.2 -- Fix browser result always returning Chromium when using `withClientHints()` -- Fix infinite-loop when await-ing `withClientHints()` in non-client-hints browser - -## Version 2.0.0-alpha.1 -- Initial work on new major version - + - Support for ES module `import { UAParser } from 'ua-parser-js'` + - Provided Enums submodule `'ua-parser-js/enums'` + - Provided Extensions submodule `'ua-parser-js/extensions'` + - Provided Helpers submodule `'ua-parser-js/helpers'` # Version 0.7 / 1.0 diff --git a/README.md b/README.md index d24cb538d..039902b95 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

@@ -15,622 +15,40 @@ # UAParser.js -JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client-Hints data that can be used either in browser (client-side) or node.js (server-side). - -* Author : Faisal Salman <> -* Demo : https://uaparser.js.org -* Source : https://github.com/faisalman/ua-parser-js -* Documentation : - * v1 : https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation - * v2 : https://docs.uaparser.js.org/v2 - -*** - -### From Our Sponsors: - - - - - - - - -
-↗ Become a sponsor -
- ---- +The most comprehensive, compact, & up-to-date JavaScript library to detect +user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser +(client-side) or node.js (server-side). # Version 2.0 -What's new & breaking, please read [CHANGELOG](CHANGELOG.md) before upgrading. +Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to +see what's new & breaking. # Documentation -### `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData` - -In browser environment you don't need to pass the user-agent string to the function, as it should automatically get the string from the `window.navigator.userAgent`. Whereas in nodejs environment, the user-agent string must be passed in order for the function to work (usually you can find the user-agent in: `request.headers["user-agent"]`). - -## Constructor - -#### * `new UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):UAParser` - -When you call `UAParser` with the `new` keyword, `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string. -Like so: - -```js -let parser = new UAParser("your user-agent here"); // you need to pass the user-agent for nodejs -console.log(parser); // {} -let parserResults = parser.getResult(); -console.log(parserResults); -/* - { - ua : "", - browser : {}, - engine : {}, - os : {}, - device : {}, - cpu : {} - } -*/ -``` - -#### * `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData` - -When you call `UAParser` without the `new` keyword, it will automatically call `getResult()` function and return the parsed results. - -```sh -returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` -``` - -## `UAParser`: - -#### Methods table -The methods are self explanatory, here's a small overview on all the available methods: - * `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os: -`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`. - - * `getBrowser()` - returns the browser name and version. - * `getDevice()` - returns the device model, type, vendor. - * `getEngine()` - returns the current browser engine name and version. - * `getOS()` - returns the running operating system name and version. - * `getCPU()` - returns CPU architectural design name. - * `getUA()` - returns the user-agent string. - * `setUA(ua)` - set a custom user-agent to be parsed. - ---- - -#### * `getResult():IData` - -```sh -returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` -``` - -#### * `getBrowser():IData` - -```sh -returns `{ name: '', version: '' }` - -# Possible 'browser.name': -2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG, -BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera, -[Mobile] Chrome [Headless/WebView], Chromium, Cobalt, Comodo Dragon, Dillo, -Dolphin, Doris, DuckDuckGo, Edge, Electron, Epiphany, Facebook, Falkon, Fennec, -Firebird, [Mobile] Firefox [Focus/Reality], Flock, Flow, GSA, GoBrowser, HeyTap, -Huawei Browser, ICE Browser, IE, IEMobile, IceApe, IceCat, IceDragon, Iceweasel, -Instagram, Iridium, Iron, Jasmine, Kakao[Story/Talk], K-Meleon, Kindle, Klar, -Konqueror, LBBROWSER, Line, LinkedIn, Links, Lunascape, Lynx, MIUI Browser, -Maemo Browser, Maemo, Maxthon, MetaSr Midori, Minimo, Mosaic, Mozilla, NetFront, -NetSurf, Netfront, Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb, -Opera Coast, Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris, -Puffin, QQ, QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, [Mobile] Safari, -Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim, -SlimBrowser, Snapchat, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser, -UP.Browser, Viera, Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, -Whale Browser, ... - -# 'browser.version' determined dynamically -``` - -#### * `getDevice():IData` - -```sh -returns `{ model: '', type: '', vendor: '' }` - -# Possible 'device.type': -console, mobile, tablet, smarttv, wearable, embedded - -########## -# NOTE: 'desktop' is not a possible device type. -# UAParser only reports info directly available from the UA string, which is not the case for 'desktop' device type. -# If you wish to detect desktop devices, you must handle the needed logic yourself. -# You can read more about it in this issue: https://github.com/faisalman/ua-parser-js/issues/182 -########## - -# Possible 'device.vendor': -Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell, -Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Infinix, Jolla, -Kobo, Lenovo, LG, Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, -OnePlus, OPPO, Ouya, Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, -Sharp, Siemens, Sony[Ericsson], Sprint, Tecno, Tesla, Vivo, Vodafone, Xbox, Xiaomi, -Zebra, ZTE, ... - -# 'device.model' determined dynamically -``` - -#### * `getEngine():IData` - -```sh -returns `{ name: '', version: '' }` - -# Possible 'engine.name' -Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, LibWeb, Links, Lynx, -NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit - -# 'engine.version' determined dynamically -``` - -#### * `getOS():IData` - -```sh -returns `{ name: '', version: '' }` - -# Possible 'os.name' -AIX, Amiga OS, Android[-x86], Arch, Bada, BeOS, BlackBerry, CentOS, Chromium OS, -Contiki, Fedora, Firefox OS, FreeBSD, Debian, Deepin, DragonFly, elementary OS, -Fuchsia, Gentoo, GhostBSD, GNU, Haiku, HarmonyOS, HP-UX, Hurd, iOS, Joli, KaiOS, -Linpus, Linspire,Linux, Mac OS, Maemo, Mageia, Mandriva, Manjaro, MeeGo, Minix, -Mint, Morph OS, NetBSD, NetRange, NetTV, Nintendo, OpenBSD, OpenVMS, OS/2, Palm, -PC-BSD, PCLinuxOS, Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS, -RISC OS, Sabayon, Sailfish, SerenityOS, Series40, Slackware, Solaris, SUSE, Symbian, -Tizen, Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], -Zenwalk, ... - -# 'os.version' determined dynamically -``` - -#### * `getCPU():IData` - -```sh -returns `{ architecture: '' }` - -# Possible 'cpu.architecture' -68k, amd64, arm[64/hf], avr, ia[32/64], irix[64], mips[64], pa-risc, ppc, sparc[64] -``` - -#### * `getUA():string` - -```sh -returns user-agent string of current instance -``` - -#### * `setUA(ua:string):UAParser` - -```sh -set user-agent string to be parsed -returns current instance -``` - ---- - -## `IData`: `since@2.0` - -#### Methods table -The methods are self explanatory, here's a small overview on all the available methods: - * `is(value)` - returns `true` if the passed value matches a value of current object, `false` otherwise - * `toString()` - returns the full-name values of current object as a string - * `withClientHints()` - returns an object with re-updated data from client hints - * `withFeatureCheck()` - returns an object with re-updated data from feature detection - ---- - -#### * `is(value:string):boolean` - -```js -// Is just a shorthand comparison to check whether the value of specified item equals one of its properties (in a case-insensitive way) -// so that instead of write it using `==` operator like this: - -let ua = UAParser(); -let device = ua.device; -let os = ua.os; - -if (device.type == "mobile" && os.name != "iOS") {} -if (device.type == "smarttv" || device.vendor == "Samsung") {} - -// we can also write the comparison above into as follow: - -if (device.is("mobile") && !os.is("iOS")) {} -if (device.is("SmartTV") || device.is("SaMsUnG")) {} - -/* - For device, properties will be checked in this particular order: type, model, vendor -*/ - -// Another examples: - -let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); - -uap.getBrowser().name; // "IEMobile" -uap.getBrowser().is("IEMobile"); // true -uap.getCPU().is("ARM"); // true - -uap.getOS().name; // "Windows Phone" -uap.getOS().is("Windows Phone"); // true - -uap.getDevice(); // { vendor: "Nokia", model: "Lumia 635", type: "mobile" } -uap.getResult().device; // { vendor: "Nokia", model: "Lumia 635", type: "mobile" } - -let device = uap.getDevice(); -device.is("mobile"); // true -device.is("Lumia 635"); // true -device.is("Nokia"); // true -device.is("iPhone"); // false -uap.getResult().device.is("Nokia"); // true -uap.getResult().device.model; // "Lumia 635" - -uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); - -let browser = uap.getBrowser(); -browser.is("IEMobile"); // false -browser.is("Chrome"); // true - -uap.getResult().browser.is("Edge"); // false -uap.getResult().os.name // "Mac OS" -uap.getResult().os.is("Mac OS"); // true -uap.getResult().os.version; // "10.6.8" - -let engine = uap.getEngine(); -engine.is("Blink"); // true -``` - -#### * `toString():string` - -```js -// Retrieve full-name values as a string - -/* - Values will be concatenated following this pattern: - * browser : name + version - * cpu : architecture - * device : vendor + model - * engine : name + version - * os : name + version -*/ - -// Usage examples - -let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); - -uap.getDevice(); // { - // vendor: "Nokia", - // model: "Lumia 635", - // type: "mobile" - // } -uap.getDevice().toString(); // "Nokia Lumia 635" - -uap.getResult().os.name; // "Windows Phone" -uap.getResult().os.version; // "8.1" -uap.getResult().os.toString(); // "Windows Phone 8.1" - -uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); -uap.getBrowser().name; // "Chrome" -uap.getBrowser().version; // "28.0.1500.95" -uap.getBrowser().major; // "28" -uap.getBrowser().toString(); // "Chrome 28.0.1500.95" - -let engine = uap.getEngine(); -engine.name; // "Blink" -engine.version; // "28.0.1500.95" -engine.toString(); // "Blink 28.0.1500.95" -``` - -#### * `withClientHints():Promise|Thenable|IData` - -Recently, Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`. -```js -// client-side example -(async function () { - let ua = new UAParser(); - - // get browser data from user-agent only : - let browser = ua.getBrowser(); - console.log('Using User-Agent: ', browser); - - // get browser data from client-hints (with user-agent as fallback) : - browser = await ua.getBrowser().withClientHints(); - console.log('Using Client-Hints: ', browser); - - // alternatively : - ua.getBrowser().withClientHints().then(function (browser) { - console.log('Using Client-Hints: ', browser); - }); -})(); -``` - -Along with `User-Agent` HTTP header, Chrome also sends this client-hints data by default under `Sec-CH-UA-*` HTTP headers in each request. In server-side development, you can capture this extra information by passing the `req.headers` to `UAParser()` (see examples below). When using `withClientHints()` in nodejs environment and browser without client-hints support (basically anything that's not Chromium-based), it will returns a new object with updated data. - -```js -// server-side example - -// Suppose we got a request having these HTTP headers: -const request = { - headers : { - 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36', - - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : 'Galaxy S3 Marketing', - 'sec-ch-ua-platform' : 'Android' - } -}; - -const result1 = UAParser(request.headers); // parse only "user-agent" header -const result2 = UAParser(request.headers).withClientHints(); // update with "sec-ch-ua" headers - -console.log(result1.os.name); // "Linux" -console.log(result1.device.type); // undefined -console.log(result1.device.model); // undefined - -console.log(result2.os.name); // "Android" -console.log(result2.device.type); // "mobile" -console.log(result2.device.model); // "Galaxy S3 Marketing" - -new UAParser(request.headers) - .getBrowser() - .withClientHints() - .then((browser) => { - console.log(browser.toString()); // Chrome 110.0.0.0 -}); -``` - -#### * `withFeatureCheck():IData` - -This method allows us to examine other features beyond `navigator.userAgent` to further improve detection of the following: -- browser : Brave (check for `navigator.isBrave`) -- device : iPad (check for `navigator.standalone` & `navigator.maxTouchPoints`) - -```js -// suppose this code runs on iPad -const withoutFeatureCheck = UAParser(); -const withFeatureCheck = UAParser().withFeatureCheck(); - -console.log(withoutFeatureCheck.device); // { vendor : "Apple", model : "Macintosh", type : undefined } -console.log(withFeatureCheck.device); // { vendor : "Apple", model : "iPad", type : "tablet" } -``` - -## Extending Regex - -If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own. - -* `UAParser([uastring,] extensions [,headers:object(since@2.0)])` - -```js -// Example: -const myOwnListOfBrowsers = [ - [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] -]; - -const myUA = 'Mozilla/5.0 MyBrowser/1.3'; - -let myParser = new UAParser({ browser: myOwnListOfBrowsers }); - -console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3", major: "1", type : "bot"} -console.log(myParser.getBrowser().is('bot')); // true - -// Another example: -const myOwnListOfDevices = [ - [/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]], - [/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]] -]; - -const myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; - -let myParser2 = new UAParser({ - browser: myOwnListOfBrowsers, - device: myOwnListOfDevices -}); - -console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"} -``` - -Some basic extensions (although not very complete at the moment) can also be found under `ua-parser-js/extensions` submodule. - -```js -import { UAParser } from 'ua-parser-js'; -import { Emails } from 'ua-parser-js/extensions'; - -const browser = new UAParser(Emails) - .setUA('Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0') - .getBrowser(); - -console.log(browser.name); // Thunderbird -``` - - -# Usage - -## Using HTML - -```html - - - - - - - - - -``` - -## Using node.js - -Note: Device information is not available in the NodeJS environment. - -```sh -$ npm install ua-parser-js -``` - -```js -var http = require('http'); -var uap = require('ua-parser-js'); - -http.createServer(function (req, res) { - // get user-agent header - var ua = uap(req.headers['user-agent']); - - /* // BEGIN since@2.0 - you can also pass client-hints data to UAParser - - // note: only works in secure context (https:// or localhost or file://) - - var getHighEntropyValues = 'Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness'; - res.setHeader('Accept-CH', getHighEntropyValues); - res.setHeader('Critical-CH', getHighEntropyValues); - - var ua = uap(req.headers).withClientHints(); - - // END since@2.0 */ - - // write the result as response - res.end(JSON.stringify(ua, null, ' ')); -}) -.listen(1337, '127.0.0.1'); - -console.log('Server running at http://127.0.0.1:1337/'); -``` - -## Using ES Modules - -```js -import { UAParser } from 'ua-parser-js'; - -const { browser, cpu, device } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900'); - -console.log(browser.name); // Maemo Browser -console.log(cpu.is('arm')); // true -console.log(device.is('mobile')); // true -console.log(device.model); // N900 -``` - -## Using TypeScript - -```sh -$ npm install --save @types/ua-parser-js -# Download TS type definition from DefinitelyTyped repository: -# https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/ua-parser-js -``` - -## Using jQuery/Zepto ($.ua) - -Although written in vanilla js, this library will automatically detect if jQuery/Zepto is present and create `$.ua` object (with values based on its User-Agent) along with `window.UAParser` constructor. To get/set user-agent you can use: `$.ua.get()` / `$.ua.set(uastring)`. - -```js -// Say we are in a browser with default user-agent: 'Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Sprint APA7373KT Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0': - -// Get the details -console.log($.ua.device); // {vendor: "HTC", model: "Evo Shift 4G", type: "mobile"} -console.log($.ua.os); // {name: "Android", version: "2.3.4"} -console.log($.ua.os.name); // "Android" -console.log($.ua.get()); // "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Sprint APA7373KT Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0" - -// Now lets try to reset to another custom user-agent -$.ua.set('Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; Xoom Build/HWI69) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13'); - -// Test again -console.log($.ua.browser.name); // "Safari" -console.log($.ua.engine.name); // "Webkit" -console.log($.ua.device); // {vendor: "Motorola", model: "Xoom", type: "tablet"} -console.log(parseInt($.ua.browser.version.split('.')[0], 10)); // 4 - -// Add class to tag -// -$('body').addClass('ua-browser-' + $.ua.browser.name + ' ua-devicetype-' + $.ua.device.type); -``` + * v1.0: https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation + * v2.0: https://docs.uaparser.js.org/v2 # Development -## Backers & Sponsors - - - - - - ## Contributors +Large or small, your contribution is valuable here. Please read [CONTRIBUTING](CONTRIBUTING.md) +guide first for the instruction details. + Made with [contributors-img](https://contrib.rocks). -## How To Contribute +## Backers & Sponsors -* Fork and clone this repository -* Make some changes as required -* Write unit test to showcase its functionality -* Run the test suites to make sure it's not breaking anything `$ npm test` -* Submit a pull request under `develop` branch + + # License -AGPL v3 License +AGPLv3 License Copyright (c) 2012-2023 Faisal Salman <> From f57f8fa1a7b53eac6c7897045b52d7e983a4b5b1 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Sep 2023 14:01:26 +0700 Subject: [PATCH 141/388] Update enums & extensions --- src/enums/ua-parser-enums.js | 281 ++++++++++++++++++------- src/extensions/ua-parser-extensions.js | 4 +- test/mocha-test-es6.mjs | 8 +- 3 files changed, 213 insertions(+), 80 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 17a518a69..d07528653 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,103 +1,236 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-alpha.3 +/* Enums for UAParser.js v2.0.0-beta https://github.com/faisalman/ua-parser-js Author: Faisal Salman - MIT License */ + AGPLv3 License */ ////////////////////////////////////////////// /*jshint esversion: 6 */ -const BrowserName = Object.freeze({ - CHROME : 'Chrome', - EDGE : 'Edge', - SAFARI : 'Safari', - FIREFOX : 'Firefox', - OPERA : 'Opera', - MOBILE_CHROME : 'Mobile Chrome', - MOBILE_SAFARI : 'Mobile Safari', - MOBILE_FIREFOX : 'Mobile Firefox', - ANDROID_BROWSER : 'Android Browser' +const Browser = Object.freeze({ + ANDROID: 'Android Browser', + BRAVE: 'Brave', + CHROME: 'Chrome', + CHROMIUM: 'Chromium', + DOLPHIN: 'Dolphin', + DUCKDUCKGO: 'DuckDuckGo', + EDGE: 'Edge', + FIREFOX: 'Firefox', + FOCUS: 'Focus', + IE: 'IE', + KONQUEROR: 'Konqueror', + MOBILE_CHROME: 'Mobile Chrome', + MOBILE_FIREFOX: 'Mobile Firefox', + MOBILE_SAFARI: 'Mobile Safari', + OPERA: 'Opera', + PALEMOON: 'PaleMoon', + PUFFIN: 'Puffin', + QQ: 'QQ Browser', + SAFARI: 'Safari', + SAMSUNG: 'Samsung Internet', + UC: 'UC Browser', + VIVALDI: 'Vivaldi', + YANDEX: 'Yandex' // TODO : test! }); -const CPUArch = Object.freeze({ - IA32 : 'ia32', - AMD64 : 'amd64', - IA64 : 'ia64', - ARM : 'arm', - ARM64 : 'arm64', - ARMHF : 'armhf', - _68K : '68k', - AVR : 'avr', - IRIX : 'irix', - IRIX64 : 'irix64', - MIPS : 'mips', - MIPS64 : 'mips64', - PPC : 'ppc', - SPARC : 'sparc', - SPARC64 : 'sparc64' +const CPU = Object.freeze({ + ARM : 'arm', + ARM64: 'arm64', + ARMHF: 'armhf', + AVR: 'avr', + IA64: 'ia64', + IRIX: 'irix', + IRIX64: 'irix64', + MIPS: 'mips', + MIPS64: 'mips64', + MOTO_68K: '68k', + PPC: 'ppc', + SPARC: 'sparc', + SPARC64: 'sparc64', + X86: 'ia32', + X86_64: 'amd64' }); -const DeviceType = Object.freeze({ - MOBILE : 'mobile', - TABLET : 'tablet', - SMARTTV : 'smarttv', - CONSOLE : 'console', - WEARABLE: 'wearable', - EMBEDDED: 'embedded' +const Device = Object.freeze({ + CONSOLE: 'console', + DEKSTOP: 'desktop', + EMBEDDED: 'embedded', + MOBILE: 'mobile', + SMARTTV: 'smarttv', + TABLET: 'tablet', + WEARABLE: 'wearable' }); -const DeviceVendor = Object.freeze({ - APPLE : 'Apple', - SAMSUNG : 'Samsung', - HUAWEI : 'Huawei', - XIAOMI : 'Xiaomi', - OPPO : 'OPPO', - VIVO : 'Vivo', - REALME : 'Realme', - LENOVO : 'Lenovo', - LG : 'LG' +const Vendor = Object.freeze({ + ACER: 'Acer', + ALCATEL: 'Alcatel', + APPLE: 'Apple', + AMAZON: 'Amazon', + ARCHOS: 'Archos', + ASUS: 'ASUS', + ATT: 'AT&T', + BENQ: 'BenQ', + BLACKBERRY: 'BlackBerry', + DELL: 'Dell', + ESSENTIAL: 'Essential', + FACEBOOK: 'Facebook', + FAIRPHONE: 'Fairphone', + GEEKSPHONE: 'GeeksPhone', + GENERIC: 'Generic', + GOOGLE: 'Google', + HP: 'HP', + HTC: 'HTC', + HUAWEI: 'Huawei', + INFINIX: 'Infinix', + JOLLA: 'Jolla', + KOBO: 'Kobo', + LENOVO: 'Lenovo', + LG: 'LG', + MEIZU: 'Meizu', + MICROSOFT: 'Microsoft', + MOTOROLA: 'Motorola', + NEXIAN: 'Nexian', + NINTENDO: 'Nintendo', + NOKIA: 'Nokia', + NVIDIA: 'Nvidia', + ONEPLUS: 'OnePlus', + OPPO: 'OPPO', + OUYA: 'Ouya', + PALM: 'Palm', + PANASONIC: 'Panasonic', + PEBBLE: 'Pebble', + POLYTRON: 'Polytron', + REALME: 'Realme', + RIM: 'RIM', + ROKU: 'Roku', + SAMSUNG: 'Samsung', + SHARP: 'Sharp', + SIEMENS: 'Siemens', + SONY: 'Sony', + SPRINT: 'Sprint', + TECNO: 'Tecno', + TESLA: 'Tesla', + ULEFONE: 'Ulefone', + VIVO: 'Vivo', + VODAFONE: 'Vodafone', + XBOX: 'Xbox', + XIAOMI: 'Xiaomi', + ZEBRA: 'Zebra', + ZTE: 'ZTE', // TODO : test! }); -const EngineName = Object.freeze({ - AMAYA : 'Amaya', - BLINK : 'Blink', +const Engine = Object.freeze({ + AMAYA: 'Amaya', + BLINK: 'Blink', EDGEHTML: 'EdgeHTML', - FLOW : 'Flow', - GECKO : 'Gecko', - GOANNA : 'Goanna', - ICAB : 'iCab', - LIBWEB : 'LibWeb', - KHTML : 'KHTML', - LINKS : 'Links', - LYNX : 'Lynx', + FLOW: 'Flow', + GECKO: 'Gecko', + GOANNA: 'Goanna', + ICAB: 'iCab', + KHTML: 'KHTML', + LIBWEB: 'LibWeb', + LINKS: 'Links', + LYNX: 'Lynx', NETFRONT: 'NetFront', - NETSURF : 'NetSurf', - PRESTO : 'Presto', - TASMAN : 'Tasman', - TRIDENT : 'Trident', - W3M : 'w3m', - WEBKIT : 'WebKit' + NETSURF: 'NetSurf', + PRESTO: 'Presto', + TASMAN: 'Tasman', + TRIDENT: 'Trident', + W3M: 'w3m', + WEBKIT: 'WebKit' }); -const OSName = Object.freeze({ - WINDOWS : 'Windows', - LINUX : 'Linux', - MACOS : 'macOS', - IOS : 'iOS', - ANDROID : 'Android' +const OS = Object.freeze({ + AIX: 'AIX', + AMIGA_OS: 'Amiga OS', + ANDROID: 'Android', + ANDROID_X86: 'Android-x86', + ARCH: 'Arch', + BADA: 'Bada', + BEOS: 'BeOS', + BLACKBERRY: 'BlackBerry', + CENTOS: 'CentOS', + CHROME_OS: 'Chrome OS', + CONTIKI: 'Contiki', + FEDORA: 'Fedora', + FIREFOX_OS: 'Firefox OS', + FREEBSD: 'FreeBSD', + DEBIAN: 'Debian', + DEEPIN: 'Deepin', + DRAGONFLY: 'DragonFly', + ELEMENTARY_OS: 'elementary OS', + FUCHSIA: 'Fuchsia', + GENTOO: 'Gentoo', + GHOSTBSD: 'GhostBSD', + GNU: 'GNU', + HAIKU: 'Haiku', + HARMONYOS: 'HarmonyOS', + HP_UX: 'HP-UX', + HURD: 'Hurd', + IOS: 'iOS', + JOLI: 'Joli', + KAIOS: 'KaiOS', + LINPUS: 'Linpus', + LINSPIRE: 'Linspire', + LINUX: 'Linux', + MACOS: 'macOS', + MAEMO: 'Maemo', + MAGEIA: 'Mageia', + MANDRIVA: 'Mandriva', + MANJARO: 'Manjaro', + MEEGO: 'MeeGo', + MINIX: 'Minix', + MINT: 'Mint', + MORPH_OS: 'Morph OS', + NETBSD: 'NetBSD', + NETRANGE: 'NetRange', + NETTV: 'NetTV', + NINTENDO: 'Nintendo', + OPENBSD: 'OpenBSD', + OPENVMS: 'OpenVMS', + OS2: 'OS/2', + PALM: 'Palm', + PC_BSD: 'PC-BSD', + PCLINUXOS: 'PCLinuxOS', + PLAN9: 'Plan9', + PLAYSTATION: 'PlayStation', + QNX: 'QNX', + RASPBIAN: 'Raspbian', + REDHAT: 'RedHat', + RIM_TABLET_OS: 'RIM Tablet OS', + RISC_OS: 'RISC OS', + SABAYON: 'Sabayon', + SAILFISH: 'Sailfish', + SERENITYOS: 'SerenityOS', + SERIES40: 'Series40', + SLACKWARE: 'Slackware', + SOLARIS: 'Solaris', + SUSE: 'SUSE', + SYMBIAN: 'Symbian', + TIZEN: 'Tizen', + UBUNTU: 'Ubuntu', + UNIX: 'Unix', + VECTORLINUX: 'VectorLinux', + VIERA: 'Viera', + WATCHOS: 'watchOS', + WEBOS: 'WebOS', + WINDOWS: 'Windows', + WINDOWS_MOBILE: 'Windows Mobile', + WINDOWS_PHONE: 'Windows Phone', + ZENWALK: 'Zenwalk' // TODO : test! }); module.exports = { - BrowserName, - CPUArch, - DeviceType, - DeviceVendor, - EngineName, - OSName + Browser, + CPU, + Device, + Vendor, + Engine, + OS }; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index aa3895bd4..5a56c2c5a 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,8 +1,8 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-alpha.3 +/* Extensions for UAParser.js v2.0.0-beta https://github.com/faisalman/ua-parser-js Author: Faisal Salman - MIT License */ + AGPLv3 License */ ////////////////////////////////////////////// /*jshint esversion: 6 */ diff --git a/test/mocha-test-es6.mjs b/test/mocha-test-es6.mjs index 8165ec996..54c4dcad3 100644 --- a/test/mocha-test-es6.mjs +++ b/test/mocha-test-es6.mjs @@ -1,5 +1,5 @@ import { UAParser } from 'ua-parser-js'; -import { CPUArch, DeviceType, EngineName } from 'ua-parser-js/enums'; +import { CPU, Device, Engine } from 'ua-parser-js/enums'; import * as assert from 'assert'; describe('Returns', () => { @@ -20,8 +20,8 @@ describe('Returns', () => { describe('Enums', () => { it('Can use enum', () => { const { cpu, device, engine } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900'); - assert.strictEqual(cpu.is(CPUArch.ARM), true); - assert.strictEqual(device.is(DeviceType.MOBILE), true); - assert.strictEqual(engine.is(EngineName.GECKO), true); + assert.strictEqual(cpu.is(CPU.ARM), true); + assert.strictEqual(device.is(Device.MOBILE), true); + assert.strictEqual(engine.is(Engine.GECKO), true); }); }); \ No newline at end of file From a9247154e08254fe7628524397596091605667a5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Sep 2023 14:11:25 +0700 Subject: [PATCH 142/388] Update build & test --- dist/ua-parser.min.js | 6 +- dist/ua-parser.pack.js | 6 +- src/enums/ua-parser-enums.mjs | 281 +++++++++++++++++------- src/extensions/ua-parser-extensions.mjs | 4 +- src/main/ua-parser.js | 6 +- src/main/ua-parser.mjs | 14 +- test/mocha-test-extension.js | 10 +- test/specs/browser-all.json | 20 ++ test/specs/cpu-all.json | 8 + test/specs/device-all.json | 72 ++++++ 10 files changed, 333 insertions(+), 94 deletions(-) diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 60de30081..e7a8735a9 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-alpha.3 +/* UAParser.js v2.0.0-beta Copyright © 2012-2023 Faisal Salman - MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-alpha.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[FORMFACTOR]){this.set(TYPE,strMapper(uaCH[FORMFACTOR],formFactorMap))}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(typeof ua===STR_TYPE)userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file + AGPLv3 License */ +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[FORMFACTOR]){this.set(TYPE,strMapper(uaCH[FORMFACTOR],formFactorMap))}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(typeof ua===STR_TYPE)userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 93cd67c45..a1a1cd6e5 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-alpha.3 +/* UAParser.js v2.0.0-beta Copyright © 2012-2023 Faisal Salman - MIT License */ -!function(i,u){"use strict";function e(i){for(var e={},t=0;t_?yi(i,_):i),this}]]).setUA(o),this}Hi.VERSION="2.0.0-alpha.3",Hi.BROWSER=e([h,m,l]),Hi.CPU=e([g]),Hi.DEVICE=e([p,t,f,o,v,a,r,x,k]),Hi.ENGINE=Hi.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Hi),exports.UAParser=Hi):typeof define===c&&define.amd?define(function(){return Hi}):typeof i!==b&&(i.UAParser=Hi);var Pi,Ui=typeof i!==b&&(i.jQuery||i.Zepto);Ui&&!Ui.ua&&(Pi=new Hi,Ui.ua=Pi.getResult(),Ui.ua.get=function(){return Pi.getUA()},Ui.ua.set=function(i){Pi.setUA(i);var e,t=Pi.getResult();for(e in t)Ui.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file + AGPLv3 License */ +!function(i,l){"use strict";function e(i){for(var e={},t=0;t_?yi(i,_):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-beta",Ui.BROWSER=e([h,m,u]),Ui.CPU=e([g]),Ui.DEVICE=e([p,t,f,o,v,a,r,x,k]),Ui.ENGINE=Ui.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===c&&define.amd?define(function(){return Ui}):typeof i!==b&&(i.UAParser=Ui);var Hi,Pi=typeof i!==b&&(i.jQuery||i.Zepto);Pi&&!Pi.ua&&(Hi=new Ui,Pi.ua=Hi.getResult(),Pi.ua.get=function(){return Hi.getUA()},Pi.ua.set=function(i){Hi.setUA(i);var e,t=Hi.getResult();for(e in t)Pi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 702119a55..788422138 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,105 +3,238 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-alpha.3 +/* Enums for UAParser.js v2.0.0-beta https://github.com/faisalman/ua-parser-js Author: Faisal Salman - MIT License */ + AGPLv3 License */ ////////////////////////////////////////////// /*jshint esversion: 6 */ -const BrowserName = Object.freeze({ - CHROME : 'Chrome', - EDGE : 'Edge', - SAFARI : 'Safari', - FIREFOX : 'Firefox', - OPERA : 'Opera', - MOBILE_CHROME : 'Mobile Chrome', - MOBILE_SAFARI : 'Mobile Safari', - MOBILE_FIREFOX : 'Mobile Firefox', - ANDROID_BROWSER : 'Android Browser' +const Browser = Object.freeze({ + ANDROID: 'Android Browser', + BRAVE: 'Brave', + CHROME: 'Chrome', + CHROMIUM: 'Chromium', + DOLPHIN: 'Dolphin', + DUCKDUCKGO: 'DuckDuckGo', + EDGE: 'Edge', + FIREFOX: 'Firefox', + FOCUS: 'Focus', + IE: 'IE', + KONQUEROR: 'Konqueror', + MOBILE_CHROME: 'Mobile Chrome', + MOBILE_FIREFOX: 'Mobile Firefox', + MOBILE_SAFARI: 'Mobile Safari', + OPERA: 'Opera', + PALEMOON: 'PaleMoon', + PUFFIN: 'Puffin', + QQ: 'QQ Browser', + SAFARI: 'Safari', + SAMSUNG: 'Samsung Internet', + UC: 'UC Browser', + VIVALDI: 'Vivaldi', + YANDEX: 'Yandex' // TODO : test! }); -const CPUArch = Object.freeze({ - IA32 : 'ia32', - AMD64 : 'amd64', - IA64 : 'ia64', - ARM : 'arm', - ARM64 : 'arm64', - ARMHF : 'armhf', - _68K : '68k', - AVR : 'avr', - IRIX : 'irix', - IRIX64 : 'irix64', - MIPS : 'mips', - MIPS64 : 'mips64', - PPC : 'ppc', - SPARC : 'sparc', - SPARC64 : 'sparc64' +const CPU = Object.freeze({ + ARM : 'arm', + ARM64: 'arm64', + ARMHF: 'armhf', + AVR: 'avr', + IA64: 'ia64', + IRIX: 'irix', + IRIX64: 'irix64', + MIPS: 'mips', + MIPS64: 'mips64', + MOTO_68K: '68k', + PPC: 'ppc', + SPARC: 'sparc', + SPARC64: 'sparc64', + X86: 'ia32', + X86_64: 'amd64' }); -const DeviceType = Object.freeze({ - MOBILE : 'mobile', - TABLET : 'tablet', - SMARTTV : 'smarttv', - CONSOLE : 'console', - WEARABLE: 'wearable', - EMBEDDED: 'embedded' +const Device = Object.freeze({ + CONSOLE: 'console', + DEKSTOP: 'desktop', + EMBEDDED: 'embedded', + MOBILE: 'mobile', + SMARTTV: 'smarttv', + TABLET: 'tablet', + WEARABLE: 'wearable' }); -const DeviceVendor = Object.freeze({ - APPLE : 'Apple', - SAMSUNG : 'Samsung', - HUAWEI : 'Huawei', - XIAOMI : 'Xiaomi', - OPPO : 'OPPO', - VIVO : 'Vivo', - REALME : 'Realme', - LENOVO : 'Lenovo', - LG : 'LG' +const Vendor = Object.freeze({ + ACER: 'Acer', + ALCATEL: 'Alcatel', + APPLE: 'Apple', + AMAZON: 'Amazon', + ARCHOS: 'Archos', + ASUS: 'ASUS', + ATT: 'AT&T', + BENQ: 'BenQ', + BLACKBERRY: 'BlackBerry', + DELL: 'Dell', + ESSENTIAL: 'Essential', + FACEBOOK: 'Facebook', + FAIRPHONE: 'Fairphone', + GEEKSPHONE: 'GeeksPhone', + GENERIC: 'Generic', + GOOGLE: 'Google', + HP: 'HP', + HTC: 'HTC', + HUAWEI: 'Huawei', + INFINIX: 'Infinix', + JOLLA: 'Jolla', + KOBO: 'Kobo', + LENOVO: 'Lenovo', + LG: 'LG', + MEIZU: 'Meizu', + MICROSOFT: 'Microsoft', + MOTOROLA: 'Motorola', + NEXIAN: 'Nexian', + NINTENDO: 'Nintendo', + NOKIA: 'Nokia', + NVIDIA: 'Nvidia', + ONEPLUS: 'OnePlus', + OPPO: 'OPPO', + OUYA: 'Ouya', + PALM: 'Palm', + PANASONIC: 'Panasonic', + PEBBLE: 'Pebble', + POLYTRON: 'Polytron', + REALME: 'Realme', + RIM: 'RIM', + ROKU: 'Roku', + SAMSUNG: 'Samsung', + SHARP: 'Sharp', + SIEMENS: 'Siemens', + SONY: 'Sony', + SPRINT: 'Sprint', + TECNO: 'Tecno', + TESLA: 'Tesla', + ULEFONE: 'Ulefone', + VIVO: 'Vivo', + VODAFONE: 'Vodafone', + XBOX: 'Xbox', + XIAOMI: 'Xiaomi', + ZEBRA: 'Zebra', + ZTE: 'ZTE', // TODO : test! }); -const EngineName = Object.freeze({ - AMAYA : 'Amaya', - BLINK : 'Blink', +const Engine = Object.freeze({ + AMAYA: 'Amaya', + BLINK: 'Blink', EDGEHTML: 'EdgeHTML', - FLOW : 'Flow', - GECKO : 'Gecko', - GOANNA : 'Goanna', - ICAB : 'iCab', - LIBWEB : 'LibWeb', - KHTML : 'KHTML', - LINKS : 'Links', - LYNX : 'Lynx', + FLOW: 'Flow', + GECKO: 'Gecko', + GOANNA: 'Goanna', + ICAB: 'iCab', + KHTML: 'KHTML', + LIBWEB: 'LibWeb', + LINKS: 'Links', + LYNX: 'Lynx', NETFRONT: 'NetFront', - NETSURF : 'NetSurf', - PRESTO : 'Presto', - TASMAN : 'Tasman', - TRIDENT : 'Trident', - W3M : 'w3m', - WEBKIT : 'WebKit' + NETSURF: 'NetSurf', + PRESTO: 'Presto', + TASMAN: 'Tasman', + TRIDENT: 'Trident', + W3M: 'w3m', + WEBKIT: 'WebKit' }); -const OSName = Object.freeze({ - WINDOWS : 'Windows', - LINUX : 'Linux', - MACOS : 'macOS', - IOS : 'iOS', - ANDROID : 'Android' +const OS = Object.freeze({ + AIX: 'AIX', + AMIGA_OS: 'Amiga OS', + ANDROID: 'Android', + ANDROID_X86: 'Android-x86', + ARCH: 'Arch', + BADA: 'Bada', + BEOS: 'BeOS', + BLACKBERRY: 'BlackBerry', + CENTOS: 'CentOS', + CHROME_OS: 'Chrome OS', + CONTIKI: 'Contiki', + FEDORA: 'Fedora', + FIREFOX_OS: 'Firefox OS', + FREEBSD: 'FreeBSD', + DEBIAN: 'Debian', + DEEPIN: 'Deepin', + DRAGONFLY: 'DragonFly', + ELEMENTARY_OS: 'elementary OS', + FUCHSIA: 'Fuchsia', + GENTOO: 'Gentoo', + GHOSTBSD: 'GhostBSD', + GNU: 'GNU', + HAIKU: 'Haiku', + HARMONYOS: 'HarmonyOS', + HP_UX: 'HP-UX', + HURD: 'Hurd', + IOS: 'iOS', + JOLI: 'Joli', + KAIOS: 'KaiOS', + LINPUS: 'Linpus', + LINSPIRE: 'Linspire', + LINUX: 'Linux', + MACOS: 'macOS', + MAEMO: 'Maemo', + MAGEIA: 'Mageia', + MANDRIVA: 'Mandriva', + MANJARO: 'Manjaro', + MEEGO: 'MeeGo', + MINIX: 'Minix', + MINT: 'Mint', + MORPH_OS: 'Morph OS', + NETBSD: 'NetBSD', + NETRANGE: 'NetRange', + NETTV: 'NetTV', + NINTENDO: 'Nintendo', + OPENBSD: 'OpenBSD', + OPENVMS: 'OpenVMS', + OS2: 'OS/2', + PALM: 'Palm', + PC_BSD: 'PC-BSD', + PCLINUXOS: 'PCLinuxOS', + PLAN9: 'Plan9', + PLAYSTATION: 'PlayStation', + QNX: 'QNX', + RASPBIAN: 'Raspbian', + REDHAT: 'RedHat', + RIM_TABLET_OS: 'RIM Tablet OS', + RISC_OS: 'RISC OS', + SABAYON: 'Sabayon', + SAILFISH: 'Sailfish', + SERENITYOS: 'SerenityOS', + SERIES40: 'Series40', + SLACKWARE: 'Slackware', + SOLARIS: 'Solaris', + SUSE: 'SUSE', + SYMBIAN: 'Symbian', + TIZEN: 'Tizen', + UBUNTU: 'Ubuntu', + UNIX: 'Unix', + VECTORLINUX: 'VectorLinux', + VIERA: 'Viera', + WATCHOS: 'watchOS', + WEBOS: 'WebOS', + WINDOWS: 'Windows', + WINDOWS_MOBILE: 'Windows Mobile', + WINDOWS_PHONE: 'Windows Phone', + ZENWALK: 'Zenwalk' // TODO : test! }); export { - BrowserName, - CPUArch, - DeviceType, - DeviceVendor, - EngineName, - OSName + Browser, + CPU, + Device, + Vendor, + Engine, + OS }; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 60e7ce16c..fd0015a4a 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,10 +3,10 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-alpha.3 +/* Extensions for UAParser.js v2.0.0-beta https://github.com/faisalman/ua-parser-js Author: Faisal Salman - MIT License */ + AGPLv3 License */ ////////////////////////////////////////////// /*jshint esversion: 6 */ diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 60dcb8101..cabc4f658 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,7 +1,7 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-alpha.3 +/* UAParser.js v2.0.0-beta Copyright © 2012-2023 Faisal Salman - MIT License *//* + AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. Demo : https://faisalman.github.io/ua-parser-js @@ -20,7 +20,7 @@ ///////////// - var LIBVERSION = '2.0.0-alpha.3', + var LIBVERSION = '2.0.0-beta', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 85c379a06..b322a7ad7 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,9 +3,9 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-alpha.3 +/* UAParser.js v2.0.0-beta Copyright © 2012-2023 Faisal Salman - MIT License *//* + AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. Demo : https://faisalman.github.io/ua-parser-js @@ -22,7 +22,7 @@ ///////////// - var LIBVERSION = '2.0.0-alpha.3', + var LIBVERSION = '2.0.0-beta', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -491,8 +491,10 @@ /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi + /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|\))/i, // Xiaomi Redmi 'numeric' models /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ @@ -507,7 +509,7 @@ ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [ // Realme - /\b(rmx[12]\d{3})(?: bui|;|\))/i + /\b(rmx[1-3]\d{3})(?: bui|;|\))/i ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ // Motorola @@ -593,6 +595,10 @@ /droid.+; (m[1-5] note) bui/i, /\bmz-([-\w]{2,})/i ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [ + + // Ulefone + /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i + ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [ // MIXED /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i, diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 605bf6389..fd29fd8a4 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -4,7 +4,7 @@ const parseJS = require('@babel/parser').parse; const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); const UAParser = require('ua-parser-js'); -const Ext = require('ua-parser-js/extensions'); +const { Bots, CLIs, Emails, Modules } = require('ua-parser-js/extensions'); describe('Bots', () => { it('Can detect bots', () => { @@ -21,7 +21,7 @@ describe('Bots', () => { const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; - const botParser = new UAParser(Ext.Bots); + const botParser = new UAParser(Bots); assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); @@ -29,16 +29,16 @@ describe('Bots', () => { assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"}); // try merging Bots & CLIs - const botsAndCLIs = { browser : [...Ext.Bots.browser, ...Ext.CLIs.browser]}; + const botsAndCLIs = { browser : [...Bots.browser, ...CLIs.browser]}; const botsAndCLIsParser = new UAParser(botsAndCLIs); assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); - const emailParser = new UAParser(Ext.Emails); + const emailParser = new UAParser(Emails); assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); - const moduleParser = new UAParser(Ext.Modules); + const moduleParser = new UAParser(Modules); assert.deepEqual(moduleParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "module"}); assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"}); assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"}); diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 2ef402e6f..ea0fd90f6 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1338,6 +1338,16 @@ "major" : "1" } }, + { + "desc" : "Vivaldi on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.88 Safari/537.36 Vivaldi/2.4.1488.36", + "expect" : + { + "name" : "Vivaldi", + "version" : "2.4.1488.36", + "major" : "2" + } + }, { "desc" : "Viera", "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", @@ -1428,6 +1438,16 @@ "major" : "6" } }, + { + "desc" : "Puffin", + "ua" : "Mozilla/5.0 (Linux; Android 7.1.1; ZTE BLADE A0620 Build/NMF26F; ru-ru) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36 Puffin/9.2.0.50586AP", + "expect" : + { + "name" : "Puffin", + "version" : "9.2.0.50586AP", + "major" : "9" + } + }, { "desc" : "Microsoft Edge 0.1", "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", diff --git a/test/specs/cpu-all.json b/test/specs/cpu-all.json index da86557f0..41df5d0fb 100644 --- a/test/specs/cpu-all.json +++ b/test/specs/cpu-all.json @@ -167,6 +167,14 @@ "architecture" : "arm64" } }, + { + "desc" : "Google Search App", + "ua" : "Mozilla/5.0 (Linux; Android 6.0; M5s Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.147 Mobile Safari/537.36 GSA/12.40.17.23.arm64", + "expect" : + { + "architecture" : "arm64" + } + }, { "desc" : "Pocket PC", "ua" : "Opera/9.7 (Windows Mobile; PPC; Opera Mobi/35166; U; en) Presto/2.2.1", diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 0e96e3673..3b96efed1 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -152,6 +152,15 @@ "type": "mobile" } }, + { + "desc": "ASUS Zenfone 2 Laser", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; ASUS_Z00ED) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "Z00ED", + "type": "mobile" + } + }, { "desc": "Acer Iconia A1-810", "ua": "Mozilla/5.0 (Linux; Android 4.2.2; A1-810 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36", @@ -971,6 +980,15 @@ "type": "tablet" } }, + { + "desc": "Lenovo IdeaTab S6000", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; IdeaTab S6000-H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 YaBrowser/18.11.1.1011.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "IdeaTab S6000-H", + "type": "tablet" + } + }, { "desc": "LG V40 ThinQ", "ua": "Mozilla/5.0 (Linux; Android 9; LM-V405) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", @@ -1592,6 +1610,15 @@ "type": "mobile" } }, + { + "desc": "OPPO F5", + "ua": "ozilla/5.0 (Linux; Android 7.1.1; CPH1723) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH1723", + "type": "mobile" + } + }, { "desc": "Realme C1", "ua": "Mozilla/5.0 (Linux; Android 8.1; RMX1811 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Mobile Safari/537.36", @@ -2330,6 +2357,15 @@ "type": "mobile" } }, + { + "desc": "Tecno Spark 8C", + "ua": "Mozilla/5.0 (Linux; Android 11; TECNO KG5n) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "KG5n", + "type": "mobile" + } + }, { "desc": "Tesla", "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1", @@ -2699,6 +2735,15 @@ "type": "mobile" } }, + { + "desc": "Xiaomi POCOPHONE F1", + "ua": "Mozilla/5.0 (Linux; Android 10; POCOPHONE F1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "POCOPHONE F1", + "type": "mobile" + } + }, { "desc": "Xiaomi Redmi 4A", "ua": "Mozilla/5.0 (Linux; Android 6.0; Redmi 4A Build/MMB29M; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", @@ -2798,6 +2843,15 @@ "type": "mobile" } }, + { + "desc": "ZTE Blade A6", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE BLADE A0620 Build/NMF26F; ru-ru) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36 Puffin/9.2.0.50586AP", + "expect": { + "vendor": "ZTE", + "model": "BLADE A0620", + "type": "mobile" + } + }, { "desc": "PlayStation 4", "ua": "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", @@ -3104,6 +3158,15 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 7", + "ua": "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 7", + "type": "mobile" + } + }, { "desc": "Generic Android Device", "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; i980 Build/MRA58K)", @@ -3334,6 +3397,15 @@ "type": "console" } }, + { + "desc": "Vivo S1 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1920) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1920", + "type": "mobile" + } + }, { "desc": "Vivo Y52s", "ua": "Mozilla/5.0 (Linux; Android 10; V2057A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.10 SP-engine/2.28.0 baiduboxapp/12.10.0.10 (Baidu; P1 10) NABar/1.0", From f6fbf170e390dc9f5d34b4167a474b04b76becd0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Sep 2023 15:42:18 +0700 Subject: [PATCH 143/388] Update formFactor to be a list --- src/main/ua-parser.js | 37 ++++++++++++++++++++---------- test/mocha-test.js | 13 +++++++++-- test/playwright-test-main.spec.mjs | 4 +++- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index cabc4f658..66c1adf5e 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -129,10 +129,14 @@ itemListToArray = function (header) { if (!header) return undefined; var arr = []; - var tokens = strip(/\\?\"/g, header).split(', '); + var tokens = strip(/\\?\"/g, header).split(','); for (var i = 0; i < tokens.length; i++) { - var token = tokens[i].split(';v='); - arr[i] = { brand : token[0], version : token[1] }; + if (tokens[i].indexOf(';') > -1) { + var token = trim(tokens[i]).split(';v='); + arr[i] = { brand : token[0], version : token[1] }; + } else { + arr[i] = tokens[i]; + } } return arr; }, @@ -157,7 +161,7 @@ return str.replace(pattern, EMPTY); }, stripQuotes = function (val) { - return typeof val === STR_TYPE ? strip(/\"/g, val) : val; + return typeof val === STR_TYPE ? strip(/\\?\"/g, val) : val; }, trim = function (str, len) { if (typeof(str) === STR_TYPE) { @@ -239,7 +243,7 @@ return (i === UNKNOWN) ? undefined : i; } } - return str; + return map.hasOwnProperty('*') ? map['*'] : str; }; /////////////// @@ -263,10 +267,11 @@ formFactorMap = { 'embedded' : 'Automotive', 'mobile' : 'Mobile', - 'tablet' : 'Tablet', + 'tablet' : ['Tablet', 'EInk'], 'smarttv' : 'TV', - 'wearable' : ['VR', 'XR'], - '?' : 'Unknown' + 'wearable' : ['VR', 'XR', 'Watch'], + '?' : ['Desktop', 'Unknown'], + '*' : undefined }; ////////////// @@ -947,7 +952,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], - [FORMFACTOR, stripQuotes(uach[CH_HEADER_FORM_FACTOR])], + [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1029,8 +1034,7 @@ }; this.parseCH = function () { - var ua = this.ua, - uaCH = this.uaCH, + var uaCH = this.uaCH, rgxMap = this.rgxMap; switch (this.itemType) { @@ -1063,7 +1067,16 @@ this.set(MODEL, uaCH[MODEL]); } if (uaCH[FORMFACTOR]) { - this.set(TYPE, strMapper(uaCH[FORMFACTOR], formFactorMap)); + var ff; + if (typeof uaCH[FORMFACTOR] !== 'string') { + var idx = 0; + while (!ff && idx < uaCH[FORMFACTOR].length) { + ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); + } + } else { + ff = strMapper(uaCH[FORMFACTOR], formFactorMap); + } + this.set(TYPE, ff); } break; case UA_OS: diff --git a/test/mocha-test.js b/test/mocha-test.js index 7c4cbde11..97c586760 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -466,17 +466,26 @@ describe('Map UA-CH headers', function () { it('Can detect form-factor from client-hints', function () { const FFVR = { - 'sec-ch-ua-form-factor' : 'VR' + 'sec-ch-ua-form-factor' : '"VR"' + }; + + const FFEInk = { + 'sec-ch-ua-form-factor' : '"Tablet", "EInk"' }; const FFUnknown = { - 'sec-ch-ua-form-factor' : 'Unknown' + 'sec-ch-ua-form-factor' : '"Unknown"' }; UAParser(FFVR).withClientHints().then(function (ua) { assert.strictEqual(ua.device.type, 'wearable'); }); + UAParser(FFEInk).withClientHints().then(function (ua) { + assert.strictEqual(ua.device.type, 'tablet'); + }); + + UAParser(FFUnknown).withClientHints().then(function (ua) { assert.strictEqual(ua.device.type, undefined); }); diff --git a/test/playwright-test-main.spec.mjs b/test/playwright-test-main.spec.mjs index bab1f3a0b..83e523845 100644 --- a/test/playwright-test-main.spec.mjs +++ b/test/playwright-test-main.spec.mjs @@ -40,7 +40,8 @@ test('read client hints data', async ({ page }) => { version: '110' } ], - platform: 'New OS' + platform: 'New OS', + formFactor: 'New Form Factor' }); } } @@ -54,6 +55,7 @@ test('read client hints data', async ({ page }) => { expect(uap).toHaveProperty('browser.name', 'New Browser'); expect(uap).toHaveProperty('os.name', 'New OS'); + expect(uap).toHaveProperty('device.type', undefined); }); test('detect Brave', async ({ page }) => { From 5a0d9cc3d09cc6ab4de3d52e6b798b898b65600e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 2 Oct 2023 14:35:29 +0700 Subject: [PATCH 144/388] Fix #655 - Provide in-package type definitions --- package.json | 3 ++ script/test-all.sh | 7 ++- src/main/ua-parser.d.ts | 104 ++++++++++++++++++++++++++++++++++++++++ test/dts-test.ts | 42 ++++++++++++++++ 4 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 src/main/ua-parser.d.ts create mode 100644 test/dts-test.ts diff --git a/package.json b/package.json index 444f87060..a62cda784 100644 --- a/package.json +++ b/package.json @@ -167,6 +167,7 @@ "Ziding Zhang " ], "type": "commonjs", + "types": "src/main/ua-parser.d.ts", "main": "src/main/ua-parser.js", "module": "src/main/ua-parser.mjs", "browser": "dist/ua-parser.pack.js", @@ -193,6 +194,7 @@ "build+test": "npm run build && npm run test", "fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync", "test": "./script/test-all.sh", + "test:dts": "tsd --typings src/main/ua-parser.d.ts --files test/dts-test.ts", "test:eslint": "eslint src && eslint script", "test:jshint": "jshint src/main", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", @@ -208,6 +210,7 @@ "mocha": "~8.2.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", + "tsd": "^0.29.0", "uglify-js": "~3.12.0" }, "repository": { diff --git a/script/test-all.sh b/script/test-all.sh index 37cbd19bf..ac766168c 100755 --- a/script/test-all.sh +++ b/script/test-all.sh @@ -24,4 +24,9 @@ npm run test:playwright || exit 1 echo ' - lint lockfile ' -npm run test:lockfile-lint || exit 1 \ No newline at end of file +npm run test:lockfile-lint || exit 1 + +echo ' +- lint d.ts files +' +npm run test:dts || exit 1 \ No newline at end of file diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts new file mode 100644 index 000000000..8bd52c57e --- /dev/null +++ b/src/main/ua-parser.d.ts @@ -0,0 +1,104 @@ +// Type definitions for UAParser.js v2.0.0-beta +// Project: https://github.com/faisalman/ua-parser-js +// Definitions by: Faisal Salman + +declare namespace UAParser { + + interface IData { + is(val: string): boolean; + toString(): string; + withClientHints(): PromiseLike | T; + withFeatureCheck(): T; + } + + interface IBrowser extends IData { + name?: string; + version?: string; + major?: string; + } + + interface ICPU extends IData { + architecture?: 'ia32' | 'ia64' | 'amd64' | 'arm' | 'arm64' | 'armhf' | 'avr' | 'irix' | 'irix64' | 'mips' | 'mips64' | '68k' | 'ppc' | 'sparc' | 'sparc64'; + } + + interface IDevice extends IData { + type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable'; + vendor?: string; + model?: string; + } + + interface IEngine extends IData { + name?: 'Amaya' | 'Blink' | 'EdgeHTML' | 'Flow' | 'Gecko' | 'Goanna' | 'iCab' | 'KHTML' | 'LibWeb' | 'Links' | 'Lynx' | 'NetFront' | 'NetSurf' | 'Presto' | 'Tasman' | 'Trident' | 'w3m' | 'WebKit'; + version?: string; + } + + interface IOS extends IData { + name?: string; + version?: string; + } + + interface IResult extends IData { + ua: string; + browser: IBrowser; + cpu: ICPU; + device: IDevice; + engine: IEngine; + os: IOS; + } + + type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; + + export function UAParser(uastring?: string, extensions?: Record, headers?: Record): IResult; + export function UAParser(uastring?: string, headers?: Record): IResult; + export function UAParser(extensions?: Record, headers?: Record): IResult; + export function UAParser(headers?: Record): IResult; + + export class UAParser { + + static readonly BROWSER: { + NAME: 'name'; + VERSION: 'version'; + MAJOR: 'major'; + }; + static readonly CPU: { + ARCHITECTURE: 'architecture'; + }; + static readonly DEVICE: { + TYPE: 'type'; + VENDOR: 'vendor'; + MODEL: 'model'; + CONSOLE: 'console'; + MOBILE: 'mobile'; + SMARTTV: 'smarttv'; + TABLET: 'tablet'; + WEARABLE: 'wearable'; + EMBEDDED: 'embedded'; + }; + static readonly ENGINE: { + NAME: 'name'; + VERSION: 'version'; + }; + static readonly OS: { + NAME: 'name'; + VERSION: 'version'; + }; + static readonly VERSION: string; + + constructor(uastring?: string, extensions?: Record, headers?: Record); + constructor(uastring?: string, headers?: Record); + constructor(extensions?: Record, headers?: Record); + constructor(headers?: Record); + + getUA(): string; + getBrowser(): IBrowser; + getCPU(): ICPU; + getDevice(): IDevice; + getEngine(): IEngine; + getOS(): IOS; + getResult(): IResult; + setUA(uastring: string): UAParser; + } +} + +export as namespace UAParser; +export = UAParser; \ No newline at end of file diff --git a/test/dts-test.ts b/test/dts-test.ts new file mode 100644 index 000000000..c060e4663 --- /dev/null +++ b/test/dts-test.ts @@ -0,0 +1,42 @@ +import { expectType } from 'tsd'; +import { UAParser, IResult, IBrowser, ICPU, IEngine, IDevice, IOS } from "../src/main/ua-parser"; + +const uastring = 'Mozilla/5.0 (X11; MyCustomOS; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0'; +const extensions = { + os : [ + [/(mycustomos)/], [UAParser.OS.NAME, [UAParser.OS.VERSION, '10']] + ] +}; +const headers = { + 'sec-ch-ua-mobile' : '?1' +}; + +expectType(UAParser()); +expectType(UAParser(uastring)); +expectType(UAParser(uastring, extensions)); +expectType(UAParser(uastring, headers)); +expectType(UAParser(extensions, headers)); +expectType(UAParser(extensions)); +expectType(UAParser(headers)); +expectType(new UAParser()); + +const parser = new UAParser(uastring); +const browser = parser.getBrowser(); + +expectType(browser); +expectType(browser.name); +expectType(browser.version); +expectType(browser.major); +expectType(browser.is('')); +expectType(browser.toString()); +expectType>(browser.withClientHints()); +expectType((browser.withClientHints()).withFeatureCheck()); +expectType((browser.withClientHints()).withFeatureCheck().is('')); + +expectType(parser.getCPU()); +expectType(parser.getDevice()); +expectType(parser.getEngine()); +expectType(parser.getOS()); +expectType(parser.getResult()); +expectType(parser.getUA()); +expectType(parser.setUA(uastring)); \ No newline at end of file From ac282df13e27f09c48567d422befc877e2c008e0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 2 Oct 2023 15:11:31 +0700 Subject: [PATCH 145/388] Update version to 2.0.0-beta.1 --- dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- package-lock.json | 1646 ++++++++++++++--------- package.js | 2 +- package.json | 6 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 2 +- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 41 +- 12 files changed, 1023 insertions(+), 694 deletions(-) diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index e7a8735a9..093a09283 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta +/* UAParser.js v2.0.0-beta.1 Copyright © 2012-2023 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(", ");for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var ua=this.ua,uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[FORMFACTOR]){this.set(TYPE,strMapper(uaCH[FORMFACTOR],formFactorMap))}break;case UA_OS:var osName=uaCH[PLATFORM];if(osName){var osVersion=uaCH[PLATFORMVER];if(osName==WINDOWS)osVersion=parseInt(majorize(osVersion),10)>=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(typeof ua===STR_TYPE)userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.1",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=tokens[i]}}return arr},lowerize=function(str){return typeof str===STR_TYPE?str.toLowerCase():str},majorize=function(version){return typeof version===STR_TYPE?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return str.replace(pattern,EMPTY)},stripQuotes=function(val){return typeof val===STR_TYPE?strip(/\\?\"/g,val):val},trim=function(str,len){if(typeof str===STR_TYPE){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[FORMFACTOR]){var ff;if(typeof uaCH[FORMFACTOR]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(typeof ua===STR_TYPE)userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index a1a1cd6e5..8013a0691 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta +/* UAParser.js v2.0.0-beta.1 Copyright © 2012-2023 Faisal Salman AGPLv3 License */ -!function(i,l){"use strict";function e(i){for(var e={},t=0;t_?yi(i,_):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-beta",Ui.BROWSER=e([h,m,u]),Ui.CPU=e([g]),Ui.DEVICE=e([p,t,f,o,v,a,r,x,k]),Ui.ENGINE=Ui.OS=e([h,m]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===c&&define.amd?define(function(){return Ui}):typeof i!==b&&(i.UAParser=Ui);var Hi,Pi=typeof i!==b&&(i.jQuery||i.Zepto);Pi&&!Pi.ua&&(Hi=new Ui,Pi.ua=Hi.getResult(),Pi.ua.get=function(){return Hi.getUA()},Pi.ua.set=function(i){Hi.setUA(i);var e,t=Hi.getResult();for(e in t)Pi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,c){"use strict";function e(i){for(var e={},t=0;t_?yi(i,_):i),this}]]).setUA(o),this}Pi.VERSION="2.0.0-beta.1",Pi.BROWSER=e([f,g,p]),Pi.CPU=e([v]),Pi.DEVICE=e([h,t,m,o,x,a,r,l,k]),Pi.ENGINE=Pi.OS=e([f,g]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Pi),exports.UAParser=Pi):typeof define===u&&define.amd?define(function(){return Pi}):typeof i!==b&&(i.UAParser=Pi);var Ui,Hi=typeof i!==b&&(i.jQuery||i.Zepto);Hi&&!Hi.ua&&(Ui=new Pi,Hi.ua=Ui.getResult(),Hi.ua.get=function(){return Ui.getUA()},Hi.ua.set=function(i){Ui.setUA(i);var e,t=Ui.getResult();for(e in t)Hi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a3c6c31ce..b6c911479 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,37 +21,23 @@ "url": "https://github.com/sponsors/faisalman" } ], - "license": "MIT", - "workspaces": [ - "src/gpu-detect", - "src/ua-client-hints", - "src/user-agent-helpers" - ], + "license": "AGPL-3.0-or-later", "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.15.4", "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", - "eslint": "^8.48.0", "jshint": "~2.13.6", "mocha": "~8.2.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", + "tsd": "^0.29.0", "uglify-js": "~3.12.0" }, "engines": { "node": "*" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -465,152 +451,6 @@ "node": ">=6.9.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", - "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", - "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -842,6 +682,18 @@ "npm": ">= 7.0.0" } }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -950,27 +802,60 @@ "fsevents": "2.3.2" } }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@tsd/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@tsd/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-VtjHPAKJqLJoHHKBDNofzvQB2+ZVxjXU/Gw6INAS9aINLQYVsxfzrQ2s84huCeYWZRTtrr7R0J7XgpZHjNwBCw==", + "dev": true, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@types/eslint": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", + "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.3.tgz", + "integrity": "sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==", + "dev": true + }, "node_modules/@types/node": { "version": "18.15.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", "dev": true }, - "node_modules/@ua-parser-js/client-hints-helpers": { - "resolved": "src/client-hints-helpers", - "link": true - }, - "node_modules/@ua-parser-js/gpu-detect": { - "resolved": "src/gpu-detect", - "link": true - }, - "node_modules/@ua-parser-js/ua-client-hints": { - "resolved": "src/ua-client-hints", - "link": true - }, - "node_modules/@ua-parser-js/user-agent-helpers": { - "resolved": "src/user-agent-helpers", - "link": true + "node_modules/@types/normalize-package-data": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.2.tgz", + "integrity": "sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==", + "dev": true }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", @@ -978,50 +863,40 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node": ">=6" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { @@ -1121,6 +996,24 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1302,15 +1195,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1320,6 +1204,23 @@ "node": ">=6" } }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001481", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", @@ -1722,20 +1623,6 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -1768,6 +1655,31 @@ "node": ">=0.10.0" } }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -1792,12 +1704,6 @@ "node": ">=4.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "node_modules/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", @@ -1846,16 +1752,25 @@ "node": ">=0.3.1" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "path-type": "^4.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, "node_modules/dom-serializer": { @@ -1941,6 +1856,15 @@ "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==", "dev": true }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1959,89 +1883,29 @@ "node": ">=0.8.0" } }, - "node_modules/eslint": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", - "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.48.0", - "@humanwhocodes/config-array": "^0.11.10", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/eslint-formatter-pretty": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-4.1.0.tgz", + "integrity": "sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "@types/eslint": "^7.2.13", + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "eslint-rule-docs": "^1.1.5", + "log-symbols": "^4.0.0", + "plur": "^4.0.0", + "string-width": "^4.2.0", + "supports-hyperlinks": "^2.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/ansi-regex": { + "node_modules/eslint-formatter-pretty/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", @@ -2050,7 +1914,7 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/ansi-styles": { + "node_modules/eslint-formatter-pretty/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -2065,13 +1929,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/eslint/node_modules/chalk": { + "node_modules/eslint-formatter-pretty/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -2087,7 +1945,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/color-convert": { + "node_modules/eslint-formatter-pretty/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -2099,85 +1957,51 @@ "node": ">=7.0.0" } }, - "node_modules/eslint/node_modules/color-name": { + "node_modules/eslint-formatter-pretty/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/eslint/node_modules/escape-string-regexp": { + "node_modules/eslint-formatter-pretty/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/eslint-formatter-pretty/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/eslint-formatter-pretty/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/eslint-formatter-pretty/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/eslint/node_modules/strip-ansi": { + "node_modules/eslint-formatter-pretty/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -2189,7 +2013,7 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/supports-color": { + "node_modules/eslint-formatter-pretty/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -2201,22 +2025,11 @@ "node": ">=8" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } + "node_modules/eslint-rule-docs": { + "version": "1.1.235", + "resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.235.tgz", + "integrity": "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==", + "dev": true }, "node_modules/esprima": { "version": "4.0.1", @@ -2231,48 +2044,6 @@ "node": ">=4" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -2291,23 +2062,21 @@ "node": ">=6" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } }, "node_modules/fastq": { "version": "1.15.0", @@ -2318,18 +2087,6 @@ "reusify": "^1.0.4" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -2373,25 +2130,6 @@ "flat": "cli.js" } }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -2482,6 +2220,12 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "node_modules/gauge": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", @@ -2637,18 +2381,32 @@ "node": ">=4" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -2658,6 +2416,27 @@ "node": ">=4.x" } }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2682,6 +2461,36 @@ "he": "bin/he" } }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -2730,29 +2539,13 @@ "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "engines": { - "node": ">=0.8.19" + "node": ">=8" } }, "node_modules/inflight": { @@ -2777,6 +2570,21 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2789,6 +2597,18 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2828,15 +2648,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -2943,6 +2754,100 @@ "node": ">=8" } }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2992,16 +2897,10 @@ "jshint": "bin/jshint" } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "node_modules/json5": { @@ -3028,19 +2927,21 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3068,12 +2969,6 @@ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, "node_modules/log-symbols": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", @@ -3180,6 +3075,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/memory-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", @@ -3212,6 +3119,75 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3245,6 +3221,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", @@ -3266,6 +3251,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/minipass": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", @@ -3487,12 +3495,6 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "dev": true }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, "node_modules/node-abi": { "version": "3.40.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz", @@ -3556,6 +3558,54 @@ "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3589,23 +3639,6 @@ "wrappy": "1" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3645,16 +3678,22 @@ "node": ">=6" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "dependencies": { - "callsites": "^3.0.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/path-exists": { @@ -3675,10 +3714,16 @@ "node": ">=0.10.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "engines": { "node": ">=8" @@ -3714,6 +3759,21 @@ "node": ">=14" } }, + "node_modules/plur": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", + "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==", + "dev": true, + "dependencies": { + "irregular-plurals": "^3.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", @@ -3740,13 +3800,30 @@ "node": ">=10" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/proper-lockfile": { @@ -3776,15 +3853,6 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3805,6 +3873,15 @@ } ] }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3838,6 +3915,141 @@ "node": ">=0.10.0" } }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", @@ -3862,6 +4074,19 @@ "node": ">=8.10.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/regexp-tree": { "version": "0.1.24", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", @@ -3899,13 +4124,21 @@ "node": ">=0.4.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/resolve": { + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/retry": { @@ -4018,27 +4251,6 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -4090,6 +4302,15 @@ "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4109,6 +4330,38 @@ "source-map": "^0.6.0" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.15.tgz", + "integrity": "sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==", + "dev": true + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -4155,6 +4408,18 @@ "node": ">=8" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", @@ -4179,6 +4444,52 @@ "node": ">=4" } }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tar": { "version": "6.1.13", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", @@ -4259,12 +4570,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -4298,40 +4603,46 @@ "node": ">=8.0" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/tsd": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.29.0.tgz", + "integrity": "sha512-5B7jbTj+XLMg6rb9sXRBGwzv7h8KJlGOkTHxY63eWpZJiQ5vJbXEjL0u7JkIxwi5EsrRE1kRVUWmy6buK/ii8A==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1" + "@tsd/typescript": "~5.2.2", + "eslint-formatter-pretty": "^4.1.0", + "globby": "^11.0.1", + "jest-diff": "^29.0.3", + "meow": "^9.0.0", + "path-exists": "^4.0.0", + "read-pkg-up": "^7.0.0" + }, + "bin": { + "tsd": "dist/cli.js" }, "engines": { - "node": ">= 0.8.0" + "node": ">=14.16" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "safe-buffer": "^5.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "*" } }, "node_modules/uglify-js": { @@ -4385,15 +4696,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -4406,6 +4708,16 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4687,10 +4999,12 @@ "src/client-hints-helpers": { "name": "@ua-parser-js/client-hints-helpers", "version": "0.0.1", + "extraneous": true, "license": "MIT" }, "src/gpu-detect": { "version": "0.0.1", + "extraneous": true, "license": "MIT" }, "src/helpers": { @@ -4702,11 +5016,13 @@ "src/ua-client-hints": { "name": "@ua-parser-js/ua-client-hints", "version": "0.0.1", + "extraneous": true, "license": "MIT" }, "src/user-agent-helpers": { "name": "@ua-parser-js/user-agent-helpers", "version": "0.0.2", + "extraneous": true, "license": "MIT", "dependencies": { "@ua-parser-js/client-hints-helpers": "*" diff --git a/package.js b/package.js index d33aba3f2..9b44ab387 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'faisalman:ua-parser-js', - version: '2.0.0-alpha.3', + version: '2.0.0-beta.1', summary: 'Lightweight JavaScript-based user-agent string parser', git: 'https://github.com/faisalman/ua-parser-js.git', documentation: 'readme.md' diff --git a/package.json b/package.json index a62cda784..92447e13f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-alpha.3", + "version": "2.0.0-beta.1", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ @@ -228,8 +228,8 @@ "test": "test" }, "bugs": "https://github.com/faisalman/ua-parser-js/issues", - "demo": "https://faisalman.github.io/ua-parser-js", - "download": "https://raw.github.com/faisalman/ua-parser-js/master/dist/ua-parser.min.js", + "demo": "https://uaparser.js.org", + "download": "https://raw.github.com/faisalman/ua-parser-js/master/dist/ua-parser.pack.js", "funding": [ { "type": "opencollective", diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index d07528653..8d6db207f 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta +/* Enums for UAParser.js v2.0.0-beta.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 788422138..83a21149c 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta +/* Enums for UAParser.js v2.0.0-beta.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5a56c2c5a..4b1d5cd86 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta +/* Extensions for UAParser.js v2.0.0-beta.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index fd0015a4a..0af69e356 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta +/* Extensions for UAParser.js v2.0.0-beta.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 8bd52c57e..c46b80c79 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0-beta +// Type definitions for UAParser.js v2.0.0-beta.1 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 66c1adf5e..c9e023198 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta +/* UAParser.js v2.0.0-beta.1 Copyright © 2012-2023 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -20,7 +20,7 @@ ///////////// - var LIBVERSION = '2.0.0-beta', + var LIBVERSION = '2.0.0-beta.1', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index b322a7ad7..abb8d6029 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta +/* UAParser.js v2.0.0-beta.1 Copyright © 2012-2023 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -22,7 +22,7 @@ ///////////// - var LIBVERSION = '2.0.0-beta', + var LIBVERSION = '2.0.0-beta.1', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -131,10 +131,14 @@ itemListToArray = function (header) { if (!header) return undefined; var arr = []; - var tokens = strip(/\\?\"/g, header).split(', '); + var tokens = strip(/\\?\"/g, header).split(','); for (var i = 0; i < tokens.length; i++) { - var token = tokens[i].split(';v='); - arr[i] = { brand : token[0], version : token[1] }; + if (tokens[i].indexOf(';') > -1) { + var token = trim(tokens[i]).split(';v='); + arr[i] = { brand : token[0], version : token[1] }; + } else { + arr[i] = tokens[i]; + } } return arr; }, @@ -159,7 +163,7 @@ return str.replace(pattern, EMPTY); }, stripQuotes = function (val) { - return typeof val === STR_TYPE ? strip(/\"/g, val) : val; + return typeof val === STR_TYPE ? strip(/\\?\"/g, val) : val; }, trim = function (str, len) { if (typeof(str) === STR_TYPE) { @@ -241,7 +245,7 @@ return (i === UNKNOWN) ? undefined : i; } } - return str; + return map.hasOwnProperty('*') ? map['*'] : str; }; /////////////// @@ -265,10 +269,11 @@ formFactorMap = { 'embedded' : 'Automotive', 'mobile' : 'Mobile', - 'tablet' : 'Tablet', + 'tablet' : ['Tablet', 'EInk'], 'smarttv' : 'TV', - 'wearable' : ['VR', 'XR'], - '?' : 'Unknown' + 'wearable' : ['VR', 'XR', 'Watch'], + '?' : ['Desktop', 'Unknown'], + '*' : undefined }; ////////////// @@ -949,7 +954,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], - [FORMFACTOR, stripQuotes(uach[CH_HEADER_FORM_FACTOR])], + [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1031,8 +1036,7 @@ }; this.parseCH = function () { - var ua = this.ua, - uaCH = this.uaCH, + var uaCH = this.uaCH, rgxMap = this.rgxMap; switch (this.itemType) { @@ -1065,7 +1069,16 @@ this.set(MODEL, uaCH[MODEL]); } if (uaCH[FORMFACTOR]) { - this.set(TYPE, strMapper(uaCH[FORMFACTOR], formFactorMap)); + var ff; + if (typeof uaCH[FORMFACTOR] !== 'string') { + var idx = 0; + while (!ff && idx < uaCH[FORMFACTOR].length) { + ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); + } + } else { + ff = strMapper(uaCH[FORMFACTOR], formFactorMap); + } + this.set(TYPE, ff); } break; case UA_OS: From 177a496c34f718d2f77083ed98807a2535c1c0d4 Mon Sep 17 00:00:00 2001 From: Ilya Daraseliya Date: Mon, 2 Oct 2023 13:54:37 +0200 Subject: [PATCH 146/388] add Klarna Shopping Browser UA parser (#669) Co-authored-by: Ilya Daraseliya --- src/main/ua-parser.js | 3 +++ test/specs/browser-all.json | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index c9e023198..bad1b7efe 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -85,6 +85,7 @@ FIREFOX = 'Firefox', OPERA = 'Opera', FACEBOOK = 'Facebook', + KLARNA = 'Klarna', WINDOWS = 'Windows'; var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ? @@ -359,6 +360,8 @@ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android ], [[NAME, FACEBOOK], VERSION], [ + /(Klarna)\/([\w\.]+)/i // Klarna Shopping Browser for iOS & Android + ], [[NAME, KLARNA], VERSION], [ /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /safari (line)\/([\w\.]+)/i, // Line App for iOS diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index ea0fd90f6..9eaa4a020 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -429,6 +429,26 @@ "major" : "undefined" } }, + { + "desc" : "Klarna in-App Browser for iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Klarna/23.36.223", + "expect" : + { + "name" : "Klarna", + "version" : "23.36.223", + "major" : "23" + } + }, + { + "desc" : "Klarna in-App Browser for Android", + "ua" : "Mozilla/5.0 (Linux; Android 12; moto g(60)s Build/S3RLS32.114-25-13; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 Klarna/23.36.215", + "expect" : + { + "name" : "Klarna", + "version" : "23.36.215", + "major" : "23" + } + }, { "desc" : "Instagram in-App Browser for iOS", "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Instagram 142.0.0.22.109 (iPhone12,5; iOS 14_1; en_US; en-US; scale=3.00; 1242x2688; 214888322) NW/1", From c8c6d121e023524a0cf901c4e1732dc9e62d0f1d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 10 Oct 2023 11:44:18 +0700 Subject: [PATCH 147/388] Add PULL_REQUEST_TEMPLATE.md --- PULL_REQUEST_TEMPLATE.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..f1a3b8dba --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,22 @@ +# Prerequisites + +- [ ] I have read and follow the contributing guidelines +- [ ] I have read and accept the [Contributor License Agreement (CLA)](https://gist.github.com/faisalman/2ed16621ebb544157eba85a7f7381417) Document and I hereby sign the CLA + +# Type of Change + +Bug fix, feature, docs update, ... + +# Description + +Please include a summary of the change (current behavior vs new behavior), which issue is fixed (you can also link to an open issue here), and why this change is necessary. + +# Test + +Please describe the tests that you ran to verify your changes. + +# Impact + +Does this PR introduce a breaking change? What changes might users need to make due to this PR? + +# Other Info \ No newline at end of file From 119515edd24fef04e898fe584ab110500ed2bc42 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 10 Oct 2023 14:14:12 +0700 Subject: [PATCH 148/388] Update readme: comparison between versions & licenses --- README.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/README.md b/README.md index 039902b95..bc6da01de 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,177 @@ user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to see what's new & breaking. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Version 1.0Version 2.0
LicenseMITAGPLv3PRO PersonalPRO BusinessPRO Enterprise
Browser detection⚠️
CPU detection⚠️
Device detection⚠️
Engine detection⚠️
OS detection⚠️
Enhanced detection⛔️
Client Hints support⛔️
Extras (Apps, Bots, Libs, Emails, Media Players, etc)⛔️
CommonJS support
ES modules support⛔️
npm module available
TypeScript declarations available⚠️
Allowed for commercial use⛔️
Permissive license⚠️
Unlimited use per 1 license⚠️
1-year support⛔️⛔️
Lifetime updates
PriceFREEFREE$12$25$500
+ GET THE PACKAGE 📥 +
+ # Documentation * v1.0: https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation From 77e0aa1ac67f5a875539b546bd2d1a5c962373b0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Oct 2023 14:18:51 +0700 Subject: [PATCH 149/388] Fix #563 #631 - Add new browser: Alipay --- src/main/ua-parser.js | 4 ++-- test/specs/browser-all.json | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index bad1b7efe..5a1832fa6 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -360,12 +360,12 @@ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android ], [[NAME, FACEBOOK], VERSION], [ - /(Klarna)\/([\w\.]+)/i // Klarna Shopping Browser for iOS & Android - ], [[NAME, KLARNA], VERSION], [ + /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android + /(alipay)client\/([\w\.]+)/i, // Alipay /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 9eaa4a020..826709ff7 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -9,6 +9,26 @@ "major" : "undefined" } }, + { + "desc" : "Alipay", + "ua" : "Mozilla/5.0 (Linux; U; Android 10; zh-CN; V2034A Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 UWS/3.22.2.33 Mobile Safari/537.36 UCBS/3.22.2.33_211025173018 NebulaSDK/1.8.100112 Nebula AlipayDefined(nt:WIFI,ws:360|0|2.0) AliApp(AP/10.2.51.7100) AlipayClient/10.2.51.7100 Language/zh-Hans useStatusBar/true isConcaveScreen/true Region/CNAriver/1.0.0", + "expect" : + { + "name" : "Alipay", + "version" : "10.2.51.7100", + "major" : "10" + } + }, + { + "desc" : "Alipay", + "ua" : "Mozilla/5.0 (Linux; Android 10; VOG-AL00 Build/HUAWEIVOG-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/105.0.5195.148 MYWeb/0.2.103.0_20230131112530 UWS/3.22.2.9999 UCBS/3.22.2.9999_220000000000 Mobile Safari/537.36 NebulaSDK/1.8.100112 Nebula AlipayDefined(nt:WIFI,ws:360|0|3.0) AliApp(AP/10.3.50.9999) AlipayClient/10.3.50.9999 Language/en isConcaveScreen/true Region/CN ProductType/devAriver/1.0.0", + "expect" : + { + "name" : "Alipay", + "version" : "10.3.50.9999", + "major" : "10" + } + }, { "desc" : "Android Browser on Galaxy Nexus", "ua" : "Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", From e614108911a947b5381f87e22b34ae33fa30de12 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Oct 2023 14:36:54 +0700 Subject: [PATCH 150/388] Increase UA_MAX_LENGTH to 500 --- src/main/ua-parser.js | 2 +- test/mocha-test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 5a1832fa6..c02c0af5f 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -41,7 +41,7 @@ WEARABLE = 'wearable', EMBEDDED = 'embedded', USER_AGENT = 'user-agent', - UA_MAX_LENGTH = 350, + UA_MAX_LENGTH = 500, BRANDS = 'brands', FORMFACTOR = 'formFactor', FULLVERLIST = 'fullVersionList', diff --git a/test/mocha-test.js b/test/mocha-test.js index 97c586760..db0c355b1 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -128,7 +128,7 @@ describe('Extending Regex', function () { }); describe('User-agent length', function () { - var UA_MAX_LENGTH = 350; + var UA_MAX_LENGTH = 500; // Real data from https://stackoverflow.com/questions/654921/how-big-can-a-user-agent-string-get#answer-6595973 var uaString = 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; (R1 1.6); SLCC1; .NET CLR 2.0.50727; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618; 66760635803; runtime 11.00294; 876906799603; 97880703; 669602703; 9778063903; 877905603; 89670803; 96690803; 8878091903; 7879040603; 999608065603; 799808803; 6666059903; 669602102803; 888809342903; 696901603; 788907703; 887806555703; 97690214703; 66760903; 968909903; 796802422703; 8868026703; 889803611803; 898706903; 977806408603; 976900799903; 9897086903; 88780803; 798802301603; 9966008603; 66760703; 97890452603; 9789064803; 96990759803; 99960107703; 8868087903; 889801155603; 78890703; 8898070603; 89970603; 89970539603; 89970488703; 8789007603; 87890903; 877904603; 9887077703; 798804903; 97890264603; 967901703; 87890703; 97690420803; 79980706603; 9867086703; 996602846703; 87690803; 6989010903; 977809603; 666601903; 876905337803; 89670603; 89970200903; 786903603; 696901911703; 788905703; 896709803; 96890703; 998601903; 88980703; 666604769703; 978806603; 7988020803; 996608803; 788903297903; 98770043603; 899708803; 66960371603; 9669088903; 69990703; 99660519903; 97780603; 888801803; 9867071703; 79780803; 9779087603; 899708603; 66960456803; 898706824603; 78890299903; 99660703; 9768079803; 977901591603; 89670605603; 787903608603; 998607934903; 799808573903; 878909603; 979808146703; 9996088603; 797803154903; 69790603; 99660565603; 7869028603; 896707703; 97980965603; 976907191703; 88680703; 888809803; 69690903; 889805523703; 899707703; 997605035603; 89970029803; 9699094903; 877906803; 899707002703; 786905857603; 69890803; 97980051903; 997603978803; 9897097903; 66960141703; 7968077603; 977804603; 88980603; 989700803; 999607887803; 78690772803; 96990560903; 98970961603; 9996032903; 9699098703; 69890655603; 978903803; 698905066803; 977806903; 9789061703; 967903747703; 976900550903; 88980934703; 8878075803; 8977028703; 97980903; 9769006603; 786900803; 98770682703; 78790903; 878906967903; 87690399603; 99860976703; 796805703; 87990603; 968906803; 967904724603; 999606603; 988705903; 989702842603; 96790603; 99760703; 88980166703; 9799038903; 98670903; 697905248603; 7968043603; 66860703; 66860127903; 9779048903; 89670123903; 78890397703; 97890603; 87890803; 8789030603; 69990603; 88880763703; 9769000603; 96990203903; 978900405903; 7869022803; 699905422903; 97890703; 87990903; 878908703; 7998093903; 898702507603; 97780637603; 966907903; 896702603; 9769004803; 7869007903; 99660158803; 7899099603; 8977055803; 99660603; 7889080903; 66660981603; 997604603; 6969089803; 899701903; 9769072703; 666603903; 99860803; 997608803; 69790903; 88680756703; 979805677903; 9986047703; 89970803; 66660603; 96690903; 8997051603; 789901209803; 8977098903; 968900326803; 87790703; 98770024803; 697901794603; 69990803; 887805925803; 968908903; 97880603; 897709148703; 877909476903; 66760197703; 977908603; 698902703; 988706504803; 977802026603; 88680964703; 8878068703; 987705107903; 978902878703; 8898069803; 9768031703; 79680803; 79980803; 669609328703; 89870238703; 99960593903; 969904218703; 78890603; 9788000703; 69690630903; 889800982903; 988709748803; 7968052803; 99960007803; 969900800803; 668604817603; 66960903; 78790734603; 8868007703; 79780034903; 8878085903; 976907603; 89670830803; 877900903; 969904889703; 7978033903; 8987043903; 99860703; 979805903; 667603803; 976805348603; 999604127603; 97790701603; 78990342903; 98770672903; 87990253903; 9877027703; 97790803; 877901895603; 8789076903; 896708595603; 997601903; 799806903; 97690603; 87790371703; 667605603; 99760303703; 97680283803; 788902750803; 787909803; 79780603; 79880866903; 9986050903; 87890543903; 979800803; 97690179703; 876901603; 699909903; 96990192603; 878904903; 877904734903; 796801446903; 977904803; 9887044803; 797805565603; 98870789703; 7869093903; 87790727703; 797801232803; 666604803; 9778071903; 9799086703; 6969000903; 89670903; 8799075903; 897708903; 88680903; 97980362603; 97980503903; 889803256703; 88980388703; 789909376803; 69690703; 6969025903; 89970309903; 96690703; 877901847803; 968901903; 96690603; 88680607603; 7889001703; 789904761803; 976807703; 976902903; 878907889703; 9897014903; 896707046603; 696909903; 666603998903; 969902703; 79680421803; 9769075603; 798800192703; 97990903; 9689024903; 668604803; 969908671903; 9996094703; 69990642703; 97890895903; 977805619903; 79980859903; 88980443803; 98970649603; 997602703; 888802169903; 699907803; 667602028803; 786903283903; 997607703; 969909803; 798809925903; 9976045603; 97790903; 9789001903; 966903603; 9789069603; 968906603; 6989091803; 896701603; 6979059803; 978803903; 997606362603; 88980803; 98970803; 88880921703; 8997065703; 899700703; 698908703; 797801027903; 7889050903; 87890603; 78690703; 99660069703; 97980309903; 976800603; 666606803; 898707703; 79880019803; 66960250803; 7978049803; 88780602603; 79680903; 88880792703; 96990903; 667608603; 87790730903; 98970903; 9699032903; 8987004803; 88880703; 89770046603; 978800803; 969908903; 9798022603; 696901903; 799803703; 989703703; 668605903; 79780903; 998601371703; 796803339703; 87890922603; 898708903; 9966061903; 66960891903; 96790903; 8779050803; 98870858803; 976909298603; 9887029903; 669608703; 979806903; 878903803; 99960703; 9789086703; 979801803; 66960008703; 979806830803; 99760212703; 786906603; 797807603; 789907297703; 96990703; 786901603; 796807766603; 896702651603; 789902585603; 66660925903; 9986085703; 66960302703; 69890703; 789900703; 89970903; 9679060703; 9789002903; 979908821603; 986708140803; 976809828703; 7988082803; 79680997903; 99960803; 9788081903; 979805703; 787908603; 66960602803; 9887098703; 978803237703; 888806804603; 999604703; 977904703; 966904635703; 97680291703; 977809345603; 8878046703; 988709803; 976900773603; 989703903; 88780198603; 87790603; 986708703; 78890604703; 87790544803; 976809850903; 887806703; 987707527603; 79880803; 9897059603; 897709820603; 97880804803; 66960026703; 9789062803; 9867090803; 669600603; 8967087703; 78890903; 89770903; 97980703; 976802687603; 66860400803; 979901288603; 96990160903; 99860228903; 966900703; 66760603; 9689035703; 9779064703; 7968023603; 87890791903; 98770870603; 9798005803; 6969087903; 9779097903; 6979065703; 699903252603; 79780989703; 87690901803; 978905763903; 977809703; 97790369703; 899703269603; 8878012703; 78790803; 87690395603; 8888042803; 667607689903; 8977041803; 6666085603; 6999080703; 69990797803; 88680721603; 99660519803; 889807603; 87890146703; 699906325903; 89770603; 669608615903; 9779028803; 88880603; 97790703; 79780703; 97680355603; 6696024803; 78790784703; 97880329903; 9699077703; 89870803; 79680227903; 976905852703; 8997098903; 896704796703; 66860598803; 9897036703; 66960703; 9699094703; 9699008703; 97780485903; 999603179903; 89770834803; 96790445603; 79680460903; 9867009603; 89870328703; 799801035803; 989702903; 66960758903; 66860150803; 6686088603; 9877092803; 96990603; 99860603; 987703663603; 98870903; 699903325603; 87790803; 97680703; 8868030703; 9799030803; 89870703; 97680803; 9669054803; 6979097603; 987708046603; 999608603; 878904803; 998607408903; 968903903; 696900703; 977907491703; 6686033803; 669601803; 99960290603; 887809169903; 979803703; 69890903; 699901447903; 8987064903; 799800603; 98770903; 8997068703; 967903603; 66760146803; 978805087903; 697908138603; 799801603; 88780964903; 989708339903; 8967048603; 88880981603; 789909703; 796806603; 977905977603; 989700603; 97780703; 9669062603; 88980714603; 897709545903; 988701916703; 667604694903; 786905664603; 877900803; 886805490903; 89970559903; 99960531803; 7998033903; 98770803; 78890418703; 669600872803; 996605216603; 78690962703; 667604903; 996600903; 999608903; 9699083803; 787901803; 97780707603; 787905312703; 977805803; 8977033703; 97890708703; 989705521903; 978800703; 698905703; 78890376903; 878907703; 999602903; 986705903; 668602719603; 979901803; 997606903; 66760393903; 987703603; 78790338903; 96890803; 97680596803; 666601603; 977902178803; 877902803; 78790038603; 8868075703; 99960060603)'; From 125f0d9f16048889f81c3d1fd35f4d49c4564f3d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Oct 2023 14:42:17 +0700 Subject: [PATCH 151/388] Fix #681 - Add new browser: Vivo Browser https://play.google.com/store/apps/details?id=com.vivo.browser --- src/main/ua-parser.js | 4 ++-- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index c02c0af5f..6206da62f 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -344,8 +344,8 @@ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ - /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser + /(oculus|samsung|sailfish|huawei|vivo)browser\/([\w\.]+)/i + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/HuaweiBrowser/VivoBrowser /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon ], [[NAME, /_/g, ' '], VERSION], [ /(electron)\/([\w\.]+) safari/i, // Electron-based App diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 826709ff7..9b95bb0a9 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1388,6 +1388,16 @@ "major" : "2" } }, + { + "desc" : "Vivo Browser", + "ua" : "Mozilla/5.0 (Linux; Android 13; 23049RAD8C; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 VivoBrowser/16.7.1.1", + "expect" : + { + "name" : "Vivo Browser", + "version" : "16.7.1.1", + "major" : "16" + } + }, { "desc" : "Viera", "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", From b51ae9eb387b54223a3c481ab78484a0337e3393 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Oct 2023 14:50:00 +0700 Subject: [PATCH 152/388] Fix misidentified WebView token as device model - found in #681 --- src/main/ua-parser.js | 4 ++-- test/specs/device-all.json | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 6206da62f..e44b3ef63 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -497,7 +497,7 @@ /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi - /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|\))/i, // Xiaomi Redmi 'numeric' models + /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad @@ -711,7 +711,7 @@ // MIXED (GENERIC) /////////////////// - /droid .+?; ([^;]+?)(?: bui|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors + /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors ], [MODEL, [TYPE, MOBILE]], [ /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors ], [MODEL, [TYPE, TABLET]], [ diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 3b96efed1..3ccb234da 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2843,6 +2843,15 @@ "type": "mobile" } }, + { + "desc": "XiaoMi Redmi Note 12 Turbo", + "ua": "Mozilla/5.0 (Linux; Android 13; 23049RAD8C; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 VivoBrowser/16.7.1.1", + "expect": { + "vendor": "Xiaomi", + "model": "23049RAD8C", + "type": "mobile" + } + }, { "desc": "ZTE Blade A6", "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE BLADE A0620 Build/NMF26F; ru-ru) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36 Puffin/9.2.0.50586AP", From 69ed6cec77685ff91cfe24fc819c9aeba21c2e31 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Oct 2023 21:59:57 +0700 Subject: [PATCH 153/388] Fix #683 - change MetaSr into Sogou Explorer (+add Sogou Mobile) --- src/main/ua-parser.js | 7 +++++-- test/specs/browser-all.json | 14 +++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index e44b3ef63..57ab56785 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -85,7 +85,7 @@ FIREFOX = 'Firefox', OPERA = 'Opera', FACEBOOK = 'Facebook', - KLARNA = 'Klarna', + SOGOU = 'Sogou', WINDOWS = 'Windows'; var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ? @@ -348,11 +348,14 @@ ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/HuaweiBrowser/VivoBrowser /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon ], [[NAME, /_/g, ' '], VERSION], [ + /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer + ], [VERSION, [NAME, SOGOU + ' Explorer']], [ + /(sogou)mo\w+\/([\d\.]+)/i // Sogou Mobile + ], [[NAME, SOGOU + ' Mobile'], VERSION], [ /(electron)\/([\w\.]+) safari/i, // Electron-based App /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla /m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser ], [NAME, VERSION], [ - /(metasr)[\/ ]?([\w\.]+)/i, // SouGouBrowser /(lbbrowser)/i, // LieBao Browser /\[(linkedin)app\]/i // LinkedIn App for iOS & Android ], [NAME], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 9b95bb0a9..efead48df 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1693,7 +1693,19 @@ "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0", "expect" : { - "name" : "MetaSr" + "name" : "Sogou Explorer", + "version" : "1.0", + "major" : "1" + } + }, + { + "desc" : "Sogou Mobile Browser", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 SogouMSE,SogouMobileBrowser/3.7.4", + "expect" : + { + "name" : "Sogou Mobile", + "version" : "3.7.4", + "major" : "3" } }, { From d565f65280a6608641ceb17a75fd630ffc6c0a85 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 19 Oct 2023 10:04:08 +0700 Subject: [PATCH 154/388] Fix #682 - Add new browser: Smart Lenovo Browser https://browser.lenovo.com.cn/ --- src/main/ua-parser.js | 5 ++++- test/specs/browser-all.json | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 57ab56785..b2b803ea8 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -70,6 +70,7 @@ BLACKBERRY = 'BlackBerry', GOOGLE = 'Google', HUAWEI = 'Huawei', + LENOVO = 'Lenovo', LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', @@ -326,6 +327,8 @@ ], [VERSION, [NAME, 'IE']], [ /ya(?:search)?browser\/([\w\.]+)/i // Yandex ], [VERSION, [NAME, 'Yandex']], [ + /slbrowser\/([\w\.]+)/i // Smart Lenovo Browser + ], [VERSION, [NAME, 'Smart ' + LENOVO + SUFFIX_BROWSER]], [ /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [ /\bfocus\/([\w\.]+)/i // Firefox Focus @@ -540,7 +543,7 @@ // Lenovo /(ideatab[-\w ]+)/i, /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i - ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ // Nokia /(?:maemo|nokia).*(n900|lumia \d+)/i, diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index efead48df..436179d67 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -748,6 +748,26 @@ "major" : "5" } }, + { + "desc" : "Smart Lenovo Browser", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.0.10171 SLBChan/8", + "expect" : + { + "name" : "Smart Lenovo Browser", + "version" : "8.0.0.10171", + "major" : "8" + } + }, + { + "desc" : "Smart Lenovo Browser", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 SLBrowser/9.0.0.9011 SLBChan/10", + "expect" : + { + "name" : "Smart Lenovo Browser", + "version" : "9.0.0.9011", + "major" : "9" + } + }, { "desc" : "LINE on Android", "ua" : "Mozilla/5.0 (Linux; Android 5.0; ASUS_Z00AD Build/LRX21V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/51.0.2704.81 Mobile Safari/537.36 Line/6.5.1/IAB", From cfc01470b5c4afdb10c32349664d10ca5e828599 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 19 Oct 2023 23:21:33 +0700 Subject: [PATCH 155/388] Update enums --- src/enums/ua-parser-enums.js | 146 +++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 16 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 8d6db207f..111e3f1a4 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -8,28 +8,137 @@ /*jshint esversion: 6 */ const Browser = Object.freeze({ + _2345_EXPLORER: '2345Explorer', + _360: '360 Browser', + ALIPAY: 'Alipay', + AMAYA: 'Amaya', ANDROID: 'Android Browser', + ARORA: 'Arora', + AVANT: 'Avant', + AVAST: 'Avast Secure Browser', + AVG: 'AVG Secure Browser', + BAIDU: 'Baidu Browser', + BASILISK: 'Basilisk', + BLAZER: 'Blazer', + BOLT: 'Bolt', + BOWSER: 'Bowser', BRAVE: 'Brave', + CAMINO: 'Camino', + CHIMERA: 'Chimera', CHROME: 'Chrome', + CHROME_HEADLESS: 'Chrome Headless', + CHROME_MOBILE: 'Mobile Chrome', + CHROME_WEBVIEW: 'Chrome WebView', CHROMIUM: 'Chromium', + COBALT: 'Cobalt', + COC_COC: 'Coc Coc', + COMODO_DRAGON: 'Comodo Dragon', + CONKEROR: 'Conkeror', + DILLO: 'Dillo', DOLPHIN: 'Dolphin', + DORIS: 'Doris', DUCKDUCKGO: 'DuckDuckGo', EDGE: 'Edge', + EPIPHANY: 'Epiphany', + FACEBOOK: 'Facebook', + FALKON: 'Falkon', + FIREBIRD: 'Firebird', FIREFOX: 'Firefox', - FOCUS: 'Focus', + FIREFOX_FOCUS: 'Firefox Focus', + FIREFOX_MOBILE: 'Mobile Firefox', + FIREFOX_REALITY: 'Firefox Reality', + FENNEC: 'Fennec', + FLOCK: 'Flock', + FLOW: 'Flow', + GO: 'Go Browser', + GOOGLE_SEARCH: 'GSA', + HEYTAP: 'HeyTap', + HUAWEI: 'Huawei Browser', + ICAB: 'iCab', + ICE: 'ICE Browser', + ICEAPE: 'IceApe', + ICECAT: 'IceCat', + ICEDRAGON: 'IceDragon', + ICEWEASEL: 'IceWeasel', IE: 'IE', + INSTAGRAM: 'Instagram', + IRIDIUM: 'Iridium', + IRON: 'Iron', + JASMINE: 'Jasmine', KONQUEROR: 'Konqueror', - MOBILE_CHROME: 'Mobile Chrome', - MOBILE_FIREFOX: 'Mobile Firefox', - MOBILE_SAFARI: 'Mobile Safari', + KAKAO: 'KakaoTalk', + KHTML: 'KHTML', + K_MELEON: 'K-Meleon', + KLAR: 'Klar', + KLARNA: 'Klarna', + KINDLE: 'Kindle', + LENOVO: 'Smart Lenovo Browser', + LIEBAO: 'LBBROWSER', + LINE: 'Line', + LINKEDIN: 'LinkedIn', + LINKS: 'Links', + LUNASCAPE: 'Lunascape', + LYNX: 'Lynx', + MAEMO: 'Maemo Browser', + MAXTHON: 'Maxthon', + MIDORI: 'Midori', + MINIMO: 'Minimo', + MIUI: 'MIUI Browser', + MOZILLA: 'Mozilla', + MOSAIC: 'Mosaic', + NAVER: 'Naver', + NETFRONT: 'NetFront', + NETSCAPE: 'Netscape', + NETSURF: 'Netsurf', + NOKIA: 'Nokia Browser', + OBIGO: 'Obigo', + OCULUS: 'Oculus Browser', + OMNIWEB: 'OmniWeb', OPERA: 'Opera', + OPERA_COAST: 'Opera Coast', + OPERA_MINI: 'Opera Mini', + OPERA_MOBI: 'Opera Mobi', + OPERA_TABLET: 'Opera Tablet', + OPERA_TOUCH: 'Opera Touch', + OVI: 'OviBrowser', PALEMOON: 'PaleMoon', + PHANTOMJS: 'PhantomJS', + PHOENIX: 'Phoenix', + POLARIS: 'Polaris', PUFFIN: 'Puffin', - QQ: 'QQ Browser', + QQ: 'QQBrowser', + QQ_LITE: 'QQBrowserLite', + QUARK: 'Quark', + QUPZILLA: 'QupZilla', + REKONQ: 'rekonq', + ROCKMELT: 'Rockmelt', SAFARI: 'Safari', + SAFARI_MOBILE: 'Mobile Safari', + SAILFISH: 'Sailfish Browser', SAMSUNG: 'Samsung Internet', - UC: 'UC Browser', + SEAMONKEY: 'SeaMonkey', + SILK: 'Silk', + SKYFIRE: 'Skyfire', + SLEIPNIR: 'Sleipnir', + SLIMBROWSER: 'SlimBrowser', + SNAPCHAT: 'Snapchat', + SOGOU_EXPLORER: 'Sogou Explorer', + SOGOU_MOBILE: 'Sogou Mobile', + SWIFTFOX: 'Swiftfox', + TESLA: 'Tesla', + TIKTOK: 'TikTok', + TIZEN: 'Tizen Browser', + UC: 'UCBrowser', + UP: 'UP.Browser', + VIERA: 'Viera', VIVALDI: 'Vivaldi', + VIVO: 'Vivo Browser', + W3M: 'w3m', + WATERFOX: 'Waterfox', + WEBKIT: 'WebKit', + WECHAT: 'WeChat', + WEIBO: 'Weibo', + WHALE: 'Whale', YANDEX: 'Yandex' // TODO : test! @@ -37,25 +146,27 @@ const Browser = Object.freeze({ const CPU = Object.freeze({ ARM : 'arm', - ARM64: 'arm64', - ARMHF: 'armhf', + ARM_64: 'arm64', + ARM_HF: 'armhf', AVR: 'avr', + AVR_32: 'avr32', IA64: 'ia64', IRIX: 'irix', - IRIX64: 'irix64', + IRIX_64: 'irix64', MIPS: 'mips', - MIPS64: 'mips64', - MOTO_68K: '68k', + MIPS_64: 'mips64', + M68K: '68k', + PA_RISC: 'pa-risc', PPC: 'ppc', SPARC: 'sparc', - SPARC64: 'sparc64', + SPARC_64: 'sparc64', X86: 'ia32', X86_64: 'amd64' }); const Device = Object.freeze({ CONSOLE: 'console', - DEKSTOP: 'desktop', + DESKTOP: 'desktop', EMBEDDED: 'embedded', MOBILE: 'mobile', SMARTTV: 'smarttv', @@ -110,6 +221,7 @@ const Vendor = Object.freeze({ SIEMENS: 'Siemens', SONY: 'Sony', SPRINT: 'Sprint', + TECHNISAT: 'TechniSAT', TECNO: 'Tecno', TESLA: 'Tesla', ULEFONE: 'Ulefone', @@ -155,14 +267,15 @@ const OS = Object.freeze({ BLACKBERRY: 'BlackBerry', CENTOS: 'CentOS', CHROME_OS: 'Chrome OS', + CHROMECAST: 'Chromecast', CONTIKI: 'Contiki', - FEDORA: 'Fedora', - FIREFOX_OS: 'Firefox OS', - FREEBSD: 'FreeBSD', DEBIAN: 'Debian', DEEPIN: 'Deepin', DRAGONFLY: 'DragonFly', ELEMENTARY_OS: 'elementary OS', + FEDORA: 'Fedora', + FIREFOX_OS: 'Firefox OS', + FREEBSD: 'FreeBSD', FUCHSIA: 'Fuchsia', GENTOO: 'Gentoo', GHOSTBSD: 'GhostBSD', @@ -221,6 +334,7 @@ const OS = Object.freeze({ WINDOWS: 'Windows', WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', + XBOX: 'Xbox', ZENWALK: 'Zenwalk' // TODO : test! From 46ff3df974c5df68fba513058c5478df5e92bade Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 19 Oct 2023 23:22:31 +0700 Subject: [PATCH 156/388] Update readme --- README.md | 28 ++++++++++++++-------------- package.json | 2 +- script/test-all.sh | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bc6da01de..2904539e1 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ see what's new & breaking. - Version 1.0 + Version 0.7 / 1.0 Version 2.0 @@ -42,7 +42,7 @@ see what's new & breaking. Browser detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -50,7 +50,7 @@ see what's new & breaking. CPU detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -58,7 +58,7 @@ see what's new & breaking. Device detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -66,7 +66,7 @@ see what's new & breaking. Engine detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -74,7 +74,7 @@ see what's new & breaking. OS detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -130,7 +130,7 @@ see what's new & breaking. TypeScript declarations available - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -147,7 +147,7 @@ see what's new & breaking. Permissive license ✅ - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -157,7 +157,7 @@ see what's new & breaking. ✅ ✅ ✅ - ⚠️ + ⚠️ ✅ @@ -178,11 +178,11 @@ see what's new & breaking. Price - FREE - FREE - $12 - $25 - $500 + FREE + FREE + $12 + $25 + $500 diff --git a/package.json b/package.json index 92447e13f..35a3f0604 100644 --- a/package.json +++ b/package.json @@ -194,7 +194,7 @@ "build+test": "npm run build && npm run test", "fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync", "test": "./script/test-all.sh", - "test:dts": "tsd --typings src/main/ua-parser.d.ts --files test/dts-test.ts", + "test:dts-lint": "tsd --typings src/main/ua-parser.d.ts --files test/dts-test.ts", "test:eslint": "eslint src && eslint script", "test:jshint": "jshint src/main", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", diff --git a/script/test-all.sh b/script/test-all.sh index ac766168c..f00fea2ed 100755 --- a/script/test-all.sh +++ b/script/test-all.sh @@ -29,4 +29,4 @@ npm run test:lockfile-lint || exit 1 echo ' - lint d.ts files ' -npm run test:dts || exit 1 \ No newline at end of file +npm run test:dts-lint || exit 1 \ No newline at end of file From 5c10e2b1070abb48f4549827533877f88139df93 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 23 Oct 2023 23:19:45 +0700 Subject: [PATCH 157/388] Improve browser detection: rename "Samsung Browser" to "Samsung Internet" https://developer.samsung.com/internet/user-agent-string-format.html --- src/main/ua-parser.js | 6 ++++-- test/specs/browser-all.json | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b2b803ea8..4995f9e9d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -347,8 +347,10 @@ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ - /(oculus|samsung|sailfish|huawei|vivo)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/HuaweiBrowser/VivoBrowser + /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser + /samsungbrowser\/([\w\.]+)/i // Samsung Internet + ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon ], [[NAME, /_/g, ' '], VERSION], [ /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 436179d67..743c8b87a 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -84,7 +84,7 @@ "ua" : "Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.0 Chrome/38.0.2125.102 Mobile Safari/537.36", "expect" : { - "name" : "Samsung Browser", + "name" : "Samsung Internet", "version" : "3.0", "major" : "3" } @@ -1189,11 +1189,41 @@ } }, { - "desc" : "Samsung Browser", + "desc" : "Samsung Internet for Android", "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG-SM-G925A Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36", "expect" : { - "name" : "Samsung Browser", + "name" : "Samsung Internet", + "version" : "4.0", + "major" : "4" + } + }, + { + "desc" : "Samsung Internet for Tizen Mobile", + "ua" : "Mozilla/5.0 (Linux; Tizen 2.3; SAMSUNG SM-Z130H) AppleWebKit/537.3 (KHTML, like Gecko) SamsungBrowser/1.0 Mobile Safari/537.3", + "expect" : + { + "name" : "Samsung Internet", + "version" : "1.0", + "major" : "1" + } + }, + { + "desc" : "Samsung Internet for Smart-TV", + "ua" : "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", + "expect" : + { + "name" : "Samsung Internet", + "version" : "1.0", + "major" : "1" + } + }, + { + "desc" : "Samsung Internet for Gear VR", + "ua" : "Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925K Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile VR Safari/537.36", + "expect" : + { + "name" : "Samsung Internet", "version" : "4.0", "major" : "4" } From f56073bb3e38e9027afee9d4df0accd768f25de6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 23 Oct 2023 23:33:04 +0700 Subject: [PATCH 158/388] Improve browser detection: remove unnecessary extra space in "Avant " --- src/main/ua-parser.js | 2 +- test/specs/browser-all.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 4995f9e9d..5cd56a014 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -304,7 +304,7 @@ /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer // Trident based - /(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser + /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 743c8b87a..45833b7bc 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -114,7 +114,7 @@ "ua" : "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB5; Avant Browser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "expect" : { - "name" : "Avant ", + "name" : "Avant", "version" : "undefined", "major" : "undefined" } From 9cc274fb6f53c3afc54c9c822d45693214050142 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 24 Oct 2023 00:00:38 +0700 Subject: [PATCH 159/388] Improve browser detection: unified name for Baidu --- src/main/ua-parser.js | 7 +-- test/specs/browser-all.json | 92 ++++++++++++++++++++++++------------- 2 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 5cd56a014..5d6c8d236 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -301,11 +301,12 @@ ], [VERSION, [NAME, OPERA]], [ // Mixed + /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu + ], [VERSION, [NAME, 'Baidu']], [ /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer // Trident based - /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser - /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser + /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon @@ -359,7 +360,7 @@ ], [[NAME, SOGOU + ' Mobile'], VERSION], [ /(electron)\/([\w\.]+) safari/i, // Electron-based App /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla - /m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser + /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser ], [NAME, VERSION], [ /(lbbrowser)/i, // LieBao Browser /\[(linkedin)app\]/i // LinkedIn App for iOS & Android diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 45833b7bc..c4c161db6 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -144,11 +144,71 @@ "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; baidubrowser 1.x)", "expect" : { - "name" : "baidubrowser", + "name" : "Baidu", "version" : "1.x", "major" : "1" } }, + { + "desc" : "Baidu", + "ua" : "Mozilla/5.0 (Linux; Android 9; Redmi Note 5 Build/PKQ1.180904.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile Safari/537.36 bdbrowser/6.4.0.4", + "expect" : + { + "name" : "Baidu", + "version" : "6.4.0.4", + "major" : "6" + } + }, + { + "desc" : "Baidu", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.4.9999.1900 Safari/537.31 BDSpark/26.4", + "expect" : + { + "name" : "Baidu", + "version" : "26.4", + "major" : "26" + } + }, + { + "desc" : "Baidu", + "ua" : "Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) BaiduHD/5.4.0.0 Mobile/10A406 Safari/8536.25", + "expect" : + { + "name" : "Baidu", + "version" : "5.4.0.0", + "major" : "5" + } + }, + { + "desc" : "BaiDu Browser", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 BIDUBrowser/8.7 Safari/537.36", + "expect" : + { + "name" : "Baidu", + "version" : "8.7", + "major" : "8" + } + }, + { + "desc" : "baidu app on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16C101 main%2F1.0 baiduboxapp/11.12.0.18 (Baidu; P2 12.1.2)", + "expect" : + { + "name" : "Baidu", + "version" : "11.12.0.18", + "major" : "11" + } + }, + { + "desc" : "baidu app on Android", + "ua" : "Mozilla/5.0 (Linux; Android 8.1.0; BKK-AL10 Build/HONORBKK-AL10; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.83 Mobile Safari/537.36 T7/11.11 baiduboxapp/11.11.0.0 (Baidu; P1 8.1.0)", + "expect" : + { + "name" : "Baidu", + "version" : "11.11.0.0", + "major" : "11" + } + }, { "desc" : "Bolt", "ua" : "Mozilla/5.0 (X11; 78; CentOS; US-en) AppleWebKit/527+ (KHTML, like Gecko) Bolt/0.862 Version/3.0 Safari/523.15", @@ -1678,26 +1738,6 @@ "major" : "6" } }, - { - "desc" : "baidu app on iOS", - "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16C101 main%2F1.0 baiduboxapp/11.12.0.18 (Baidu; P2 12.1.2)", - "expect" : - { - "name" : "baiduboxapp", - "version" : "11.12.0.18", - "major" : "11" - } - }, - { - "desc" : "baidu app on Android", - "ua" : "Mozilla/5.0 (Linux; Android 8.1.0; BKK-AL10 Build/HONORBKK-AL10; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.83 Mobile Safari/537.36 T7/11.11 baiduboxapp/11.11.0.0 (Baidu; P1 8.1.0)", - "expect" : - { - "name" : "baiduboxapp", - "version" : "11.11.0.0", - "major" : "11" - } - }, { "desc" : "WeChat Desktop for Windows Built-in Browser", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", @@ -1766,16 +1806,6 @@ "name" : "LBBROWSER" } }, - { - "desc" : "BaiDu Browser", - "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 BIDUBrowser/8.7 Safari/537.36", - "expect" : - { - "name" : "BIDUBrowser", - "version" : "8.7", - "major" : "8" - } - }, { "desc" : "2345 Browser", "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.90 Safari/537.36 2345Explorer/9.2.1.17116", From 17f0c1e1cd4c6c63a726ab4b360a880fa5e91478 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 24 Oct 2023 00:09:53 +0700 Subject: [PATCH 160/388] Improve browser detection: WeChat --- src/main/ua-parser.js | 13 ++++++------- test/specs/browser-all.json | 4 ++-- test/specs/os-all.json | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 5d6c8d236..1314d9d99 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -318,8 +318,7 @@ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser ], [VERSION, [NAME, 'UCBrowser']], [ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser - /\bqbcore\/([\w\.]+).+microm/i - ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [ + /\bqbcore\/([\w\.]+).+microm/i, /micromessenger\/([\w\.]+)/i // WeChat ], [VERSION, [NAME, 'WeChat']], [ /konqueror\/([\w\.]+)/i // Konqueror @@ -757,12 +756,12 @@ // Windows /microsoft (windows) (vista|xp)/i // Windows (iTunes) ], [NAME, VERSION], [ - /(windows) nt 6\.2; (arm)/i, // Windows RT - /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, // Windows Phone - /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i + /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i // Windows Phone ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ - /(win(?=3|9|n)|win 9x )([nt\d\.]+)/i - ], [[NAME, WINDOWS], [VERSION, strMapper, windowsVersionMap]], [ + /windows nt 6\.2; (arm)/i, // Windows RT + /windows[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i, + /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i + ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ // iOS/macOS /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index c4c161db6..98e5f6ca1 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1743,7 +1743,7 @@ "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", "expect" : { - "name" : "WeChat(Win) Desktop", + "name" : "WeChat", "version" : "3.43.901.400", "major" : "3" } @@ -1753,7 +1753,7 @@ "ua" : "mozilla/5.0 (windows nt 10.0; wow64) applewebkit/537.36 (khtml, like gecko) chrome/53.0.2785.116 safari/537.36 qbcore/4.0.1301.400 qqbrowser/9.0.2524.400 mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/81.0.4044.138 safari/537.36 nettype/wifi micromessenger/7.0.20.1781(0x6700143b) windowswechat", "expect" : { - "name" : "WeChat(Win) Desktop", + "name" : "WeChat", "version" : "4.0.1301.400", "major" : "4" } diff --git a/test/specs/os-all.json b/test/specs/os-all.json index 5bf7630b0..9ad108cbb 100644 --- a/test/specs/os-all.json +++ b/test/specs/os-all.json @@ -80,6 +80,24 @@ "version" : "10" } }, + { + "desc" : "WeChat Desktop for Windows Built-in Browser", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "WeChat Desktop for Windows Built-in Browser major version in 4", + "ua" : "mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/81.0.4044.138 safari/537.36 nettype/wifi micromessenger/7.0.20.1781(0x6700143b) windowswechat", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, { "desc" : "Windows RT", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", From f8f71c65d48292dbd30ed1db6c2dfb1e9dadac61 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 24 Oct 2023 11:14:28 +0700 Subject: [PATCH 161/388] Add new helpers submodule --- package.json | 4 ++++ script/build-module.js | 6 ++++++ src/helpers/ua-parser-helpers.d.ts | 11 +++++++++++ src/helpers/ua-parser-helpers.js | 20 ++++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 src/helpers/ua-parser-helpers.d.ts create mode 100644 src/helpers/ua-parser-helpers.js diff --git a/package.json b/package.json index 35a3f0604..15c4746bb 100644 --- a/package.json +++ b/package.json @@ -183,6 +183,10 @@ "./extensions": { "require": "./src/extensions/ua-parser-extensions.js", "import": "./src/extensions/ua-parser-extensions.mjs" + }, + "./helpers": { + "require": "./src/extensions/ua-parser-helpers.js", + "import": "./src/extensions/ua-parser-helpers.mjs" } }, "files": [ diff --git a/script/build-module.js b/script/build-module.js index f8079233c..98fb8dbb2 100755 --- a/script/build-module.js +++ b/script/build-module.js @@ -46,6 +46,12 @@ const modules = [ dest : 'src/extensions/ua-parser-extensions.mjs', title : 'ua-parser-js/extensions', replacements : [] + }, + { + src : 'src/helpers/ua-parser-helpers.js', + dest : 'src/helpers/ua-parser-helpers.mjs', + title : 'ua-parser-js/helpers', + replacements : [] } ]; diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts new file mode 100644 index 000000000..6a0e5705f --- /dev/null +++ b/src/helpers/ua-parser-helpers.d.ts @@ -0,0 +1,11 @@ +// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.1 +// Project: https://github.com/faisalman/ua-parser-js +// Definitions by: Faisal Salman + +import { IResult } from "../main/ua-parser"; + +declare function isAppleSilicon(res:IResult): boolean; + +export { + isAppleSilicon +} \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js new file mode 100644 index 000000000..180c8f211 --- /dev/null +++ b/src/helpers/ua-parser-helpers.js @@ -0,0 +1,20 @@ +/////////////////////////////////////////////// +/* Helpers for UAParser.js v2.0.0-beta.1 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +const { CPU, OS } = require('../enums/ua-parser-enums'); + +const isAppleSilicon = function (res) { + return res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); +} + +module.exports = { + isAppleSilicon +} + +// TODO: create test \ No newline at end of file From 26f7e7d59e58b5d14957cc6496b7cff339372915 Mon Sep 17 00:00:00 2001 From: Danila Rodichkin Date: Mon, 6 Nov 2023 10:05:06 +0300 Subject: [PATCH 162/388] Add "types" inside "exports" of package.json, fix ./helpers import typo (#688) --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) mode change 100644 => 100755 package.json diff --git a/package.json b/package.json old mode 100644 new mode 100755 index 15c4746bb..529eb89a9 --- a/package.json +++ b/package.json @@ -174,7 +174,8 @@ "exports": { ".": { "require": "./src/main/ua-parser.js", - "import": "./src/main/ua-parser.mjs" + "import": "./src/main/ua-parser.mjs", + "types": "./src/main/ua-parser.d.ts" }, "./enums": { "require": "./src/enums/ua-parser-enums.js", @@ -185,8 +186,9 @@ "import": "./src/extensions/ua-parser-extensions.mjs" }, "./helpers": { - "require": "./src/extensions/ua-parser-helpers.js", - "import": "./src/extensions/ua-parser-helpers.mjs" + "require": "./src/helpers/ua-parser-helpers.js", + "import": "./src/helpers/ua-parser-helpers.mjs", + "types": "./src/helpers/ua-parser-helpers.d.ts" } }, "files": [ From 7ad3e3b4517eff243d940a564a01da1ca2eaf53e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 26 Oct 2023 11:41:06 +0700 Subject: [PATCH 163/388] Internal refactor: new helper methods to check for string & window --- src/main/ua-parser.js | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 1314d9d99..11ff26510 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -19,7 +19,6 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-beta.1', EMPTY = '', UNKNOWN = '?', @@ -89,7 +88,8 @@ SOGOU = 'Sogou', WINDOWS = 'Windows'; - var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ? + var isWindow = typeof window !== UNDEF_TYPE, + NAVIGATOR = (isWindow && window.navigator) ? window.navigator : undefined, NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ? @@ -121,13 +121,16 @@ } return false; } - return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; + return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, isExtensions = function (obj) { for (var prop in obj) { return /^(browser|cpu|device|engine|os)$/.test(prop); } }, + isString = function (val) { + return typeof val === STR_TYPE; + }, itemListToArray = function (header) { if (!header) return undefined; var arr = []; @@ -143,10 +146,10 @@ return arr; }, lowerize = function (str) { - return typeof(str) === STR_TYPE ? str.toLowerCase() : str; + return isString(str) ? str.toLowerCase() : str; }, majorize = function (version) { - return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; + return isString(version) ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; }, setProps = function (arr) { for (var i in arr) { @@ -160,15 +163,15 @@ return this; }, strip = function (pattern, str) { - return str.replace(pattern, EMPTY); + return isString(str) ? str.replace(pattern, EMPTY) : str; }, - stripQuotes = function (val) { - return typeof val === STR_TYPE ? strip(/\\?\"/g, val) : val; + stripQuotes = function (str) { + return strip(/\\?\"/g, str); }, trim = function (str, len) { - if (typeof(str) === STR_TYPE) { + if (isString(str)) { str = strip(/^\s\s*/, str); - return typeof(len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); + return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); } }; @@ -1191,7 +1194,7 @@ ['getResult', createItemFunc(UA_RESULT)], ['getUA', function () { return userAgent; }], ['setUA', function (ua) { - if (typeof ua === STR_TYPE) + if (isString(ua)) userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua; return this; }] @@ -1212,7 +1215,7 @@ ////////// // check js environment - if (typeof(exports) !== UNDEF_TYPE) { + if (typeof exports !== UNDEF_TYPE) { // nodejs env if (typeof module !== UNDEF_TYPE && module.exports) { exports = module.exports = UAParser; @@ -1220,11 +1223,11 @@ exports.UAParser = UAParser; } else { // requirejs env (optional) - if (typeof(define) === FUNC_TYPE && define.amd) { + if (typeof define === FUNC_TYPE && define.amd) { define(function () { return UAParser; }); - } else if (typeof window !== UNDEF_TYPE) { + } else if (isWindow) { // browser env window.UAParser = UAParser; } @@ -1235,7 +1238,7 @@ // In AMD env the global scope should be kept clean, but jQuery is an exception. // jQuery always exports to global scope, unless jQuery.noConflict(true) is used, // and we should catch that. - var $ = typeof window !== UNDEF_TYPE && (window.jQuery || window.Zepto); + var $ = isWindow && (window.jQuery || window.Zepto); if ($ && !$.ua) { var parser = new UAParser(); $.ua = parser.getResult(); From 7abc8b9ecc816ac30ad746cac7014a9111e6d02a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 9 Nov 2023 13:41:37 +0700 Subject: [PATCH 164/388] Add new helper method `isChromiumBased()` to check whether the current browser is a Chromium-based browser --- package-lock.json | 4 ++-- src/helpers/ua-parser-helpers.d.ts | 6 ++++-- src/helpers/ua-parser-helpers.js | 11 ++++++----- test/playwright-test-main.spec.mjs | 1 - 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index b6c911479..2ae23f28c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-alpha.3", + "version": "2.0.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-alpha.3", + "version": "2.0.0-beta.1", "funding": [ { "type": "opencollective", diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 6a0e5705f..4a233af13 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -4,8 +4,10 @@ import { IResult } from "../main/ua-parser"; -declare function isAppleSilicon(res:IResult): boolean; +declare function isAppleSilicon(res: IResult): boolean; +declare function isChromiumBased(res: IResult): boolean; export { - isAppleSilicon + isAppleSilicon, + isChromiumBased } \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 180c8f211..302a9e9d6 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -7,14 +7,15 @@ /*jshint esversion: 6 */ -const { CPU, OS } = require('../enums/ua-parser-enums'); +const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); -const isAppleSilicon = function (res) { - return res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); -} +const isAppleSilicon = (res) => res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); + +const isChromiumBased = (res) => res.engine.is(Engine.BLINK); module.exports = { - isAppleSilicon + isAppleSilicon, + isChromiumBased } // TODO: create test \ No newline at end of file diff --git a/test/playwright-test-main.spec.mjs b/test/playwright-test-main.spec.mjs index 83e523845..982e9429f 100644 --- a/test/playwright-test-main.spec.mjs +++ b/test/playwright-test-main.spec.mjs @@ -1,4 +1,3 @@ -// @ts-check import { test, expect } from '@playwright/test'; import path from 'path'; import url from 'url'; From 106d882fba0849f810416deaf3043f8ffefc0824 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 10 Nov 2023 10:10:38 +0700 Subject: [PATCH 165/388] Create test for helpers --- src/helpers/ua-parser-helpers.js | 4 +--- test/dts-test.ts | 8 +++++++- test/mocha-test-helpers.js | 26 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 test/mocha-test-helpers.js diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 302a9e9d6..e11a42145 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -16,6 +16,4 @@ const isChromiumBased = (res) => res.engine.is(Engine.BLINK); module.exports = { isAppleSilicon, isChromiumBased -} - -// TODO: create test \ No newline at end of file +} \ No newline at end of file diff --git a/test/dts-test.ts b/test/dts-test.ts index c060e4663..6ede7002b 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -1,5 +1,6 @@ import { expectType } from 'tsd'; import { UAParser, IResult, IBrowser, ICPU, IEngine, IDevice, IOS } from "../src/main/ua-parser"; +import { isAppleSilicon, isChromiumBased } from "../src/helpers/ua-parser-helpers"; const uastring = 'Mozilla/5.0 (X11; MyCustomOS; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0'; const extensions = { @@ -39,4 +40,9 @@ expectType(parser.getEngine()); expectType(parser.getOS()); expectType(parser.getResult()); expectType(parser.getUA()); -expectType(parser.setUA(uastring)); \ No newline at end of file +expectType(parser.setUA(uastring)); + +const result = parser.getResult(); + +expectType(isAppleSilicon(result)); +expectType(isChromiumBased(result)); \ No newline at end of file diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js new file mode 100644 index 000000000..9e27d8985 --- /dev/null +++ b/test/mocha-test-helpers.js @@ -0,0 +1,26 @@ +const assert = require('assert'); +const UAParser = require('ua-parser-js'); +const { isAppleSilicon, isChromiumBased } = require('ua-parser-js/helpers'); + +describe('isAppleSilicon', () => { + it('Can detect Apple Silicon device', () => { + + // non-real ua + const macARM = 'Mozilla/5.0 (Macintosh; ARM; Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0'; + const macIntel = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0'; + + assert.equal(isAppleSilicon(UAParser(macIntel)), false); + assert.equal(isAppleSilicon(UAParser(macARM)), true); + }); +}); + +describe('isChromiumBased', () => { + it('Can detect Chromium-based browser', () => { + + const edge = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.2151.58'; + const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; + + assert.equal(isChromiumBased(UAParser(edge)), true); + assert.equal(isChromiumBased(UAParser(firefox)), false); + }); +}); \ No newline at end of file From 5173a5442f4af04b4f9c51519587e7969de9fc15 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 23 Nov 2023 12:03:53 +0700 Subject: [PATCH 166/388] Update readme for clarity over license options --- README.md | 41 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 2904539e1..e1afb0594 100644 --- a/README.md +++ b/README.md @@ -19,23 +19,21 @@ The most comprehensive, compact, & up-to-date JavaScript library to detect user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser (client-side) or node.js (server-side). -# Version 2.0 -Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to -see what's new & breaking. +## License Options - - + + - + - + @@ -145,9 +143,9 @@ see what's new & breaking. - + - + @@ -188,12 +186,16 @@ see what's new & breaking.
Version 0.7 / 1.0Version 2.0Open-Source EditionsPRO / Commercial Editions
LicenseLicense options MITAGPLv3AGPL PRO Personal PRO Business PRO Enterprise
Permissive licensePermissive (non-copyleft) license ⚠️⛔️
- GET THE PACKAGE 📥 + GET THE PRO PACKAGES 📥
+## Version 2.0 +Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to +see what's new & breaking. + # Documentation * v1.0: https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation @@ -216,22 +218,3 @@ Made with [contributors-img](https://contrib.rocks). - -# License - -AGPLv3 License - -Copyright (c) 2012-2023 Faisal Salman <> - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see http://www.gnu.org/licenses/. From 2046fe52090b28f13daf9e4812a7ac7577fc2fea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:46:31 +0700 Subject: [PATCH 167/388] Bump @babel/traverse from 7.15.4 to 7.23.2 (#684) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.15.4 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 225 ++++++++++++++++------------------------------ package.json | 2 +- 2 files changed, 77 insertions(+), 150 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ae23f28c..cf50e1b37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "license": "AGPL-3.0-or-later", "devDependencies": { "@babel/parser": "7.15.8", - "@babel/traverse": "7.15.4", + "@babel/traverse": "7.23.2", "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", "jshint": "~2.13.6", @@ -52,12 +52,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -114,34 +115,13 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/core/node_modules/@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.21.4", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -170,34 +150,34 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -234,39 +214,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-simple-access": { "version": "7.20.2", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", @@ -280,30 +227,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -332,47 +279,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helpers/node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -392,23 +306,23 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template/node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -418,18 +332,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", - "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-function-name": "^7.15.4", - "@babel/helper-hoist-variables": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -437,14 +352,26 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { diff --git a/package.json b/package.json index 529eb89a9..b4611ea1d 100755 --- a/package.json +++ b/package.json @@ -209,7 +209,7 @@ }, "devDependencies": { "@babel/parser": "7.15.8", - "@babel/traverse": "7.15.4", + "@babel/traverse": "7.23.2", "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", "jshint": "~2.13.6", From fdbeabbaed7056b0d68a2cb137f57a5ea4308b03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:47:35 +0700 Subject: [PATCH 168/388] Bump axios from 1.3.6 to 1.6.1 (#689) Bumps [axios](https://github.com/axios/axios) from 1.3.6 to 1.6.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.3.6...v1.6.1) --- updated-dependencies: - dependency-name: axios dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf50e1b37..72b415e19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -948,9 +948,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz", - "integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", From e4f2463849886aea3d0000a9e3b60409e73f0710 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 30 Nov 2023 11:12:28 +0700 Subject: [PATCH 169/388] Create declaration file .d.ts for extensions submodule --- package.json | 3 ++- src/extensions/ua-parser-extensions.d.ts | 13 +++++++++++++ src/main/ua-parser.d.ts | 10 ++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 src/extensions/ua-parser-extensions.d.ts diff --git a/package.json b/package.json index b4611ea1d..7f8a20f5e 100755 --- a/package.json +++ b/package.json @@ -183,7 +183,8 @@ }, "./extensions": { "require": "./src/extensions/ua-parser-extensions.js", - "import": "./src/extensions/ua-parser-extensions.mjs" + "import": "./src/extensions/ua-parser-extensions.mjs", + "types": "./src/extensions/ua-parser-extensions.d.ts" }, "./helpers": { "require": "./src/helpers/ua-parser-helpers.js", diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts new file mode 100644 index 000000000..12750c804 --- /dev/null +++ b/src/extensions/ua-parser-extensions.d.ts @@ -0,0 +1,13 @@ +// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.1 +// Project: https://github.com/faisalman/ua-parser-js +// Definitions by: Faisal Salman + +import type { UAParserExt } from "../main/ua-parser"; + +export const Apps: UAParserExt; +export const Bots: UAParserExt; +export const CLIs: UAParserExt; +export const ExtraDevices: UAParserExt; +export const Emails: UAParserExt; +export const MediaPlayers: UAParserExt; +export const Modules: UAParserExt; \ No newline at end of file diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index c46b80c79..2d6f9b671 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -47,10 +47,12 @@ declare namespace UAParser { } type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; + type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os'; + type UAParserExt = Record; - export function UAParser(uastring?: string, extensions?: Record, headers?: Record): IResult; + export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record): IResult; export function UAParser(uastring?: string, headers?: Record): IResult; - export function UAParser(extensions?: Record, headers?: Record): IResult; + export function UAParser(extensions?: UAParserExt, headers?: Record): IResult; export function UAParser(headers?: Record): IResult; export class UAParser { @@ -84,9 +86,9 @@ declare namespace UAParser { }; static readonly VERSION: string; - constructor(uastring?: string, extensions?: Record, headers?: Record); + constructor(uastring?: string, extensions?: UAParserExt, headers?: Record); constructor(uastring?: string, headers?: Record); - constructor(extensions?: Record, headers?: Record); + constructor(extensions?: UAParserExt, headers?: Record); constructor(headers?: Record); getUA(): string; From 3622b614a71749e6d96f241d6810d6086075e2a4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 30 Nov 2023 11:50:44 +0700 Subject: [PATCH 170/388] Fix d.ts Record for extensions as Partial --- src/main/ua-parser.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 2d6f9b671..3fa574945 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -48,7 +48,7 @@ declare namespace UAParser { type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os'; - type UAParserExt = Record; + type UAParserExt = Partial>; export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record): IResult; export function UAParser(uastring?: string, headers?: Record): IResult; From 09904a0a4713971e7f0650fc20534749e99289e4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 20 Dec 2023 22:19:42 +0700 Subject: [PATCH 171/388] Fix undefined brandName when reading a field list that has no version --- src/main/ua-parser.js | 2 +- test/mocha-test.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 11ff26510..98e3b8128 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1056,7 +1056,7 @@ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; if (brands) { for (var i in brands) { - var brandName = brands[i].brand, + var brandName = brands[i].brand || brands[i], brandVersion = brands[i].version; if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { this.set(NAME, strip(GOOGLE+' ', brandName)) diff --git a/test/mocha-test.js b/test/mocha-test.js index db0c355b1..13fddc7da 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -490,4 +490,26 @@ describe('Map UA-CH headers', function () { assert.strictEqual(ua.device.type, undefined); }); }); + + it('Avoid error on headers variation', function () { + + const headers2 = { + 'sec-ch-ua' : '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"', + 'sec-ch-ua-full-version-list' : '"Google Chrome", "Chromium", "Not?A_Brand";v="24.0.0.0"', + 'sec-ch-ua-full-version' : '""', + 'sec-ch-ua-mobile' : '?0', + 'sec-ch-ua-arch' : '""', + 'sec-ch-ua-bitness' : '""', + 'sec-ch-ua-model' : '""', + 'sec-ch-ua-platform' : '"Windows"', + 'sec-ch-ua-platform-version' : '""', + 'sec-ch-ua-wow64' : '?0', + }; + + uap = UAParser(headers2).withClientHints(); + + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, undefined); + assert.strictEqual(uap.browser.major, undefined); + }); }); \ No newline at end of file From 0c49d7507473e5ae26678fd41c7ca548e431c80b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 29 Dec 2023 20:54:56 +0700 Subject: [PATCH 172/388] Fix #697 - Add new browser: Opera GX - https://www.opera.com/gx --- src/main/ua-parser.js | 2 ++ test/specs/browser-all.json | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 98e3b8128..9133442ca 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -300,6 +300,8 @@ ], [NAME, VERSION], [ /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0 ], [VERSION, [NAME, OPERA+' Mini']], [ + /\bop(?:rg)?x\/([\w\.]+)/i // Opera GX + ], [VERSION, [NAME, OPERA+' GX']], [ /\bopr\/([\w\.]+)/i // Opera Webkit ], [VERSION, [NAME, OPERA]], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 98e5f6ca1..bae2f9982 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1138,6 +1138,26 @@ "major" : "12" } }, + { + "desc" : "Opera GX on Android", + "ua" : "Mozilla/5.0 (Linux; Android 10; Redmi Note 8 Pro Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.168 Mobile Safari/537.36 OPX/2", + "expect" : + { + "name" : "Opera GX", + "version" : "2", + "major" : "2" + } + }, + { + "desc" : "Opera GX on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 OPR/60.0.3255.50747 OPRGX/60.0.3255.50747", + "expect" : + { + "name" : "Opera GX", + "version" : "60.0.3255.50747", + "major" : "60" + } + }, { "desc" : "Opera Tablet", "ua" : "Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1", From b5c62b0c827ae091a706f9109faad110ad7cdf3f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 10 Jan 2024 17:14:22 +0700 Subject: [PATCH 173/388] Fix #635 - ua-ch: prioritize more specific brand name regardless the order --- src/main/ua-parser.js | 11 ++++++----- test/mocha-test.js | 46 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 9133442ca..0fc46e51b 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -140,7 +140,7 @@ var token = trim(tokens[i]).split(';v='); arr[i] = { brand : token[0], version : token[1] }; } else { - arr[i] = tokens[i]; + arr[i] = trim(tokens[i]); } } return arr; @@ -1055,15 +1055,16 @@ switch (this.itemType) { case UA_BROWSER: - var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; + var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; if (brands) { for (var i in brands) { - var brandName = brands[i].brand || brands[i], + var brandName = strip(GOOGLE+' ', brands[i].brand || brands[i]), brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { - this.set(NAME, strip(GOOGLE+' ', brandName)) + if (!/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && !/chromi/i.test(brandName)))) { + this.set(NAME, brandName) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); + prevName = brandName; } } } diff --git a/test/mocha-test.js b/test/mocha-test.js index 13fddc7da..e81810575 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -512,4 +512,50 @@ describe('Map UA-CH headers', function () { assert.strictEqual(uap.browser.version, undefined); assert.strictEqual(uap.browser.major, undefined); }); + + it('Prioritize more specific brand name regardless the order', function () { + + const headers3a = { + 'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132"' + }; + const headers3b = { + 'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Not_A Brand;v=8, Google Chrome;v=120.0.6099.132"' + }; + const headers3c = { + 'sec-ch-ua-full-version-list' : '"Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"' + }; + const headers3d = { + 'sec-ch-ua-full-version-list' : '"Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"' + }; + const headers3e = { + 'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132, Microsoft Edge;v=120.0.6099.133, Not_A Brand;v=8"' + }; + const headers3f = { + 'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131"' + }; + + uap = UAParser(headers3a).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3b).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3c).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3d).withClientHints(); + assert.strictEqual(uap.browser.name, "Microsoft Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); + + uap = UAParser(headers3e).withClientHints(); + assert.strictEqual(uap.browser.name, "Microsoft Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); + + uap = UAParser(headers3f).withClientHints(); + assert.strictEqual(uap.browser.name, "Microsoft Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); + }); }); \ No newline at end of file From 9c5d6ee70e61dacd8ff485f9ba6ff513c2370cc4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 18 Jan 2024 11:23:59 +0700 Subject: [PATCH 174/388] Fix Edge detection in ua-ch: "Microsoft Edge" -> "Edge" --- src/main/ua-parser.js | 2 +- test/mocha-test.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0fc46e51b..9abeb701f 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1058,7 +1058,7 @@ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; if (brands) { for (var i in brands) { - var brandName = strip(GOOGLE+' ', brands[i].brand || brands[i]), + var brandName = strip(/(Google|Microsoft) /, brands[i].brand || brands[i]), brandVersion = brands[i].version; if (!/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && !/chromi/i.test(brandName)))) { this.set(NAME, brandName) diff --git a/test/mocha-test.js b/test/mocha-test.js index e81810575..0de0efe1d 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -547,15 +547,15 @@ describe('Map UA-CH headers', function () { assert.strictEqual(uap.browser.version, "120.0.6099.132"); uap = UAParser(headers3d).withClientHints(); - assert.strictEqual(uap.browser.name, "Microsoft Edge"); + assert.strictEqual(uap.browser.name, "Edge"); assert.strictEqual(uap.browser.version, "120.0.6099.133"); uap = UAParser(headers3e).withClientHints(); - assert.strictEqual(uap.browser.name, "Microsoft Edge"); + assert.strictEqual(uap.browser.name, "Edge"); assert.strictEqual(uap.browser.version, "120.0.6099.133"); uap = UAParser(headers3f).withClientHints(); - assert.strictEqual(uap.browser.name, "Microsoft Edge"); + assert.strictEqual(uap.browser.name, "Edge"); assert.strictEqual(uap.browser.version, "120.0.6099.133"); }); }); \ No newline at end of file From 54c633aac50dec2801a78a88b37e25dac4ec04c6 Mon Sep 17 00:00:00 2001 From: Beat YT <66485277+Beat-YT@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:24:38 -0500 Subject: [PATCH 175/388] Update ua-parser.js (#696) Fixed Xbox Detection for Chrome-Based Edge --- src/main/ua-parser.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 9abeb701f..8283c653b 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1083,6 +1083,11 @@ if (uaCH[MODEL]) { this.set(MODEL, uaCH[MODEL]); } + // Xbox-Specific Detection + if (uaCH[MODEL] == 'Xbox') { + this.set(TYPE, CONSOLE); + this.set(VENDOR, MICROSOFT); + } if (uaCH[FORMFACTOR]) { var ff; if (typeof uaCH[FORMFACTOR] !== 'string') { @@ -1104,6 +1109,11 @@ this.set(NAME, osName) .set(VERSION, osVersion); } + // Xbox-Specific Detection + if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') { + this.set(NAME, 'Xbox') + this.set(VERSION, undefined) + } break; case UA_RESULT: var data = this.data; From d6d8ac7cb43d610e54e1e4355da35b0e7ea76a43 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 24 Jan 2024 09:56:23 +0700 Subject: [PATCH 176/388] Fix #692 - Improve TS module resolution (#702) --- package.json | 41 ++++++++++++++++++++++++++++++----------- src/main/ua-parser.js | 6 +++--- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 7f8a20f5e..1e4292181 100755 --- a/package.json +++ b/package.json @@ -173,23 +173,42 @@ "browser": "dist/ua-parser.pack.js", "exports": { ".": { - "require": "./src/main/ua-parser.js", - "import": "./src/main/ua-parser.mjs", - "types": "./src/main/ua-parser.d.ts" + "require": { + "default": "./src/main/ua-parser.js", + "types": "./src/main/ua-parser.d.ts" + }, + "import": { + "default": "./src/main/ua-parser.mjs", + "types": "./src/main/ua-parser.d.ts" + } }, "./enums": { - "require": "./src/enums/ua-parser-enums.js", - "import": "./src/enums/ua-parser-enums.mjs" + "require": { + "default": "./src/enums/ua-parser-enums.js" + }, + "import": { + "default": "./src/enums/ua-parser-enums.mjs" + } }, "./extensions": { - "require": "./src/extensions/ua-parser-extensions.js", - "import": "./src/extensions/ua-parser-extensions.mjs", - "types": "./src/extensions/ua-parser-extensions.d.ts" + "require": { + "default": "./src/extensions/ua-parser-extensions.js", + "types": "./src/extensions/ua-parser-extensions.d.ts" + }, + "import": { + "default": "./src/extensions/ua-parser-extensions.mjs", + "types": "./src/extensions/ua-parser-extensions.d.ts" + } }, "./helpers": { - "require": "./src/helpers/ua-parser-helpers.js", - "import": "./src/helpers/ua-parser-helpers.mjs", - "types": "./src/helpers/ua-parser-helpers.d.ts" + "require": { + "default": "./src/helpers/ua-parser-helpers.js", + "types": "./src/helpers/ua-parser-helpers.d.ts" + }, + "import": { + "default": "./src/helpers/ua-parser-helpers.mjs", + "types": "./src/helpers/ua-parser-helpers.d.ts" + } } }, "files": [ diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 8283c653b..df3c2dab2 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1085,8 +1085,8 @@ } // Xbox-Specific Detection if (uaCH[MODEL] == 'Xbox') { - this.set(TYPE, CONSOLE); - this.set(VENDOR, MICROSOFT); + this.set(TYPE, CONSOLE) + .set(VENDOR, MICROSOFT); } if (uaCH[FORMFACTOR]) { var ff; @@ -1112,7 +1112,7 @@ // Xbox-Specific Detection if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') { this.set(NAME, 'Xbox') - this.set(VERSION, undefined) + .set(VERSION, undefined); } break; case UA_RESULT: From 7c22bc587f64abba4e1bbe7f354d4260892f02b2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 25 Jan 2024 11:27:39 +0700 Subject: [PATCH 177/388] Fix #703 - Improve TS module resolution --revert --- package.json | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 1e4292181..7f8a20f5e 100755 --- a/package.json +++ b/package.json @@ -173,42 +173,23 @@ "browser": "dist/ua-parser.pack.js", "exports": { ".": { - "require": { - "default": "./src/main/ua-parser.js", - "types": "./src/main/ua-parser.d.ts" - }, - "import": { - "default": "./src/main/ua-parser.mjs", - "types": "./src/main/ua-parser.d.ts" - } + "require": "./src/main/ua-parser.js", + "import": "./src/main/ua-parser.mjs", + "types": "./src/main/ua-parser.d.ts" }, "./enums": { - "require": { - "default": "./src/enums/ua-parser-enums.js" - }, - "import": { - "default": "./src/enums/ua-parser-enums.mjs" - } + "require": "./src/enums/ua-parser-enums.js", + "import": "./src/enums/ua-parser-enums.mjs" }, "./extensions": { - "require": { - "default": "./src/extensions/ua-parser-extensions.js", - "types": "./src/extensions/ua-parser-extensions.d.ts" - }, - "import": { - "default": "./src/extensions/ua-parser-extensions.mjs", - "types": "./src/extensions/ua-parser-extensions.d.ts" - } + "require": "./src/extensions/ua-parser-extensions.js", + "import": "./src/extensions/ua-parser-extensions.mjs", + "types": "./src/extensions/ua-parser-extensions.d.ts" }, "./helpers": { - "require": { - "default": "./src/helpers/ua-parser-helpers.js", - "types": "./src/helpers/ua-parser-helpers.d.ts" - }, - "import": { - "default": "./src/helpers/ua-parser-helpers.mjs", - "types": "./src/helpers/ua-parser-helpers.d.ts" - } + "require": "./src/helpers/ua-parser-helpers.js", + "import": "./src/helpers/ua-parser-helpers.mjs", + "types": "./src/helpers/ua-parser-helpers.d.ts" } }, "files": [ From b5b5475ab4b302ba41347d313c628f7655926c97 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 25 Jan 2024 12:21:17 +0700 Subject: [PATCH 178/388] Add new helper method: `isFrozenUA()` to match with frozen user-agent pattern --- src/helpers/ua-parser-helpers.d.ts | 4 +++- src/helpers/ua-parser-helpers.js | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 4a233af13..c7e9b1e07 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -6,8 +6,10 @@ import { IResult } from "../main/ua-parser"; declare function isAppleSilicon(res: IResult): boolean; declare function isChromiumBased(res: IResult): boolean; +declare function isFrozenUA(ua: string): boolean; export { isAppleSilicon, - isChromiumBased + isChromiumBased, + isFrozenUA } \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index e11a42145..350b02988 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -13,7 +13,10 @@ const isAppleSilicon = (res) => res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); const isChromiumBased = (res) => res.engine.is(Engine.BLINK); +const isFrozenUA = (ua) => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); + module.exports = { isAppleSilicon, - isChromiumBased + isChromiumBased, + isFrozenUA } \ No newline at end of file From 4d950db14572c84aaceea7e7368abe9ccb8b2687 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 28 Jan 2024 22:34:46 +0700 Subject: [PATCH 179/388] Update version to 2.0.0-beta.2 --- CHANGELOG.md | 44 +++++++ dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- package-lock.json | 4 +- package.js | 2 +- package.json | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 148 ++++++++++++++++++++--- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 2 +- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 26 ++++ src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 97 +++++++++------ test/specs/browser-all.json | 10 ++ 18 files changed, 290 insertions(+), 69 deletions(-) create mode 100644 src/helpers/ua-parser-helpers.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 84bcaf048..b3541fe62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,54 @@ - Provided Extensions submodule `'ua-parser-js/extensions'` - Provided Helpers submodule `'ua-parser-js/helpers'` +## Version 2.0.0-beta.2 + +- Increase UA_MAX_LENGTH to 500 +- Add TypeScript declaration file in `ua-parser-js/extensions` submodule +- Improve TypeScript module resolution +- Add new methods in `ua-parser-js/helpers` submodule: `isAppleSilicon()` & `isChromiumBased()` +- Fix misidentified WebView token as device model +- Add new browser: Alipay, Klarna, Opera GX, Smart Lenovo Browser, Vivo Browser +- Rename browser: Avant, Baidu, Samsung Internet, Sogou Explorer, Sogou Mobile, WeChat +- Improve client-hints detection: Edge, Xbox + +## Version 2.0.0-beta.1 + +- Update Client Hints Form-Factor +- Provide in-package type definitions +- Add new device: Ulefone +- Improve device detection: Realme, Xiaomi Redmi + +## Version 2.0.0-alpha.3 + +- Add `withFeatureCheck()` method +- Add `isFrozenUA()` method in `ua-parser-js/helpers` submodule +- Add `MediaPlayers` & `Modules` in `ua-parser-js/extensions` submodule +- Fix issue with ESM import + +## Version 2.0.0-alpha.2 + +- Fix browser result always returning Chromium when using withClientHints() +- Fix infinite-loop when await-ing withClientHints() in non-client-hints browser + +## Version 2.0.0-alpha.1 + +- Initial work on new major version + + # Version 0.7 / 1.0 Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. +## Version 0.7.37 + +- Fix misidentified WebView token as device model +- Increase UA_MAX_LENGTH to 500 +- Add new browser: Alipay, Klarna, Smart Lenovo Browser, Vivo Browser +- Add new device: Ulefone +- Improve device detection: Realme, Xiaomi Redmi +- Rename browser: Avant, Baidu, Samsung Internet, Sogou Explorer, Sogou Mobile, WeChat + ## Version 0.7.36 / 1.0.36 - Add new browser: Snapchat - Add new devices: Infinix, Tecno diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 093a09283..f23c2c4e9 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta.1 +/* UAParser.js v2.0.0-beta.2 Copyright © 2012-2023 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.1",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=350,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",WINDOWS="Windows";var NAVIGATOR=typeof window!==UNDEF_TYPE&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return typeof str1===STR_TYPE?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=tokens[i]}}return arr},lowerize=function(str){return typeof str===STR_TYPE?str.toLowerCase():str},majorize=function(version){return typeof version===STR_TYPE?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return str.replace(pattern,EMPTY)},stripQuotes=function(val){return typeof val===STR_TYPE?strip(/\\?\"/g,val):val},trim=function(str,len){if(typeof str===STR_TYPE){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS];if(brands){for(var i in brands){var brandName=brands[i].brand,brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(i<1||/chromi/i.test(this.get(NAME)))){this.set(NAME,strip(GOOGLE+" ",brandName)).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[FORMFACTOR]){var ff;if(typeof uaCH[FORMFACTOR]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(typeof ua===STR_TYPE)userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTOR]){var ff;if(typeof uaCH[FORMFACTOR]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 8013a0691..320b5712a 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta.1 +/* UAParser.js v2.0.0-beta.2 Copyright © 2012-2023 Faisal Salman AGPLv3 License */ -!function(i,c){"use strict";function e(i){for(var e={},t=0;t_?yi(i,_):i),this}]]).setUA(o),this}Pi.VERSION="2.0.0-beta.1",Pi.BROWSER=e([f,g,p]),Pi.CPU=e([v]),Pi.DEVICE=e([h,t,m,o,x,a,r,l,k]),Pi.ENGINE=Pi.OS=e([f,g]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Pi),exports.UAParser=Pi):typeof define===u&&define.amd?define(function(){return Pi}):typeof i!==b&&(i.UAParser=Pi);var Ui,Hi=typeof i!==b&&(i.jQuery||i.Zepto);Hi&&!Hi.ua&&(Ui=new Pi,Hi.ua=Ui.getResult(),Hi.ua.get=function(){return Ui.getUA()},Hi.ua.set=function(i){Ui.setUA(i);var e,t=Ui.getResult();for(e in t)Hi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,u){"use strict";function e(i){for(var e={},t=0;t_?Ti(i,_):i),this}]]).setUA(o),this}Hi.VERSION="2.0.0-beta.2",Hi.BROWSER=e([f,v,p]),Hi.CPU=e([x]),Hi.DEVICE=e([h,g,m,k,y,t,r,o,a]),Hi.ENGINE=Hi.OS=e([f,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Hi),exports.UAParser=Hi):typeof define===c&&define.amd?define(function(){return Hi}):di&&(i.UAParser=Hi);var Ei,Mi=di&&(i.jQuery||i.Zepto);Mi&&!Mi.ua&&(Ei=new Hi,Mi.ua=Ei.getResult(),Mi.ua.get=function(){return Ei.getUA()},Mi.ua.set=function(i){Ei.setUA(i);var e,t=Ei.getResult();for(e in t)Mi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 72b415e19..818a3bc68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "funding": [ { "type": "opencollective", diff --git a/package.js b/package.js index 9b44ab387..d58154c96 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'faisalman:ua-parser-js', - version: '2.0.0-beta.1', + version: '2.0.0-beta.2', summary: 'Lightweight JavaScript-based user-agent string parser', git: 'https://github.com/faisalman/ua-parser-js.git', documentation: 'readme.md' diff --git a/package.json b/package.json index 7f8a20f5e..09bfd49ff 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 111e3f1a4..8930a2712 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta.1 +/* Enums for UAParser.js v2.0.0-beta.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 83a21149c..756194259 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta.1 +/* Enums for UAParser.js v2.0.0-beta.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -12,28 +12,137 @@ /*jshint esversion: 6 */ const Browser = Object.freeze({ + _2345_EXPLORER: '2345Explorer', + _360: '360 Browser', + ALIPAY: 'Alipay', + AMAYA: 'Amaya', ANDROID: 'Android Browser', + ARORA: 'Arora', + AVANT: 'Avant', + AVAST: 'Avast Secure Browser', + AVG: 'AVG Secure Browser', + BAIDU: 'Baidu Browser', + BASILISK: 'Basilisk', + BLAZER: 'Blazer', + BOLT: 'Bolt', + BOWSER: 'Bowser', BRAVE: 'Brave', + CAMINO: 'Camino', + CHIMERA: 'Chimera', CHROME: 'Chrome', + CHROME_HEADLESS: 'Chrome Headless', + CHROME_MOBILE: 'Mobile Chrome', + CHROME_WEBVIEW: 'Chrome WebView', CHROMIUM: 'Chromium', + COBALT: 'Cobalt', + COC_COC: 'Coc Coc', + COMODO_DRAGON: 'Comodo Dragon', + CONKEROR: 'Conkeror', + DILLO: 'Dillo', DOLPHIN: 'Dolphin', + DORIS: 'Doris', DUCKDUCKGO: 'DuckDuckGo', EDGE: 'Edge', + EPIPHANY: 'Epiphany', + FACEBOOK: 'Facebook', + FALKON: 'Falkon', + FIREBIRD: 'Firebird', FIREFOX: 'Firefox', - FOCUS: 'Focus', + FIREFOX_FOCUS: 'Firefox Focus', + FIREFOX_MOBILE: 'Mobile Firefox', + FIREFOX_REALITY: 'Firefox Reality', + FENNEC: 'Fennec', + FLOCK: 'Flock', + FLOW: 'Flow', + GO: 'Go Browser', + GOOGLE_SEARCH: 'GSA', + HEYTAP: 'HeyTap', + HUAWEI: 'Huawei Browser', + ICAB: 'iCab', + ICE: 'ICE Browser', + ICEAPE: 'IceApe', + ICECAT: 'IceCat', + ICEDRAGON: 'IceDragon', + ICEWEASEL: 'IceWeasel', IE: 'IE', + INSTAGRAM: 'Instagram', + IRIDIUM: 'Iridium', + IRON: 'Iron', + JASMINE: 'Jasmine', KONQUEROR: 'Konqueror', - MOBILE_CHROME: 'Mobile Chrome', - MOBILE_FIREFOX: 'Mobile Firefox', - MOBILE_SAFARI: 'Mobile Safari', + KAKAO: 'KakaoTalk', + KHTML: 'KHTML', + K_MELEON: 'K-Meleon', + KLAR: 'Klar', + KLARNA: 'Klarna', + KINDLE: 'Kindle', + LENOVO: 'Smart Lenovo Browser', + LIEBAO: 'LBBROWSER', + LINE: 'Line', + LINKEDIN: 'LinkedIn', + LINKS: 'Links', + LUNASCAPE: 'Lunascape', + LYNX: 'Lynx', + MAEMO: 'Maemo Browser', + MAXTHON: 'Maxthon', + MIDORI: 'Midori', + MINIMO: 'Minimo', + MIUI: 'MIUI Browser', + MOZILLA: 'Mozilla', + MOSAIC: 'Mosaic', + NAVER: 'Naver', + NETFRONT: 'NetFront', + NETSCAPE: 'Netscape', + NETSURF: 'Netsurf', + NOKIA: 'Nokia Browser', + OBIGO: 'Obigo', + OCULUS: 'Oculus Browser', + OMNIWEB: 'OmniWeb', OPERA: 'Opera', + OPERA_COAST: 'Opera Coast', + OPERA_MINI: 'Opera Mini', + OPERA_MOBI: 'Opera Mobi', + OPERA_TABLET: 'Opera Tablet', + OPERA_TOUCH: 'Opera Touch', + OVI: 'OviBrowser', PALEMOON: 'PaleMoon', + PHANTOMJS: 'PhantomJS', + PHOENIX: 'Phoenix', + POLARIS: 'Polaris', PUFFIN: 'Puffin', - QQ: 'QQ Browser', + QQ: 'QQBrowser', + QQ_LITE: 'QQBrowserLite', + QUARK: 'Quark', + QUPZILLA: 'QupZilla', + REKONQ: 'rekonq', + ROCKMELT: 'Rockmelt', SAFARI: 'Safari', + SAFARI_MOBILE: 'Mobile Safari', + SAILFISH: 'Sailfish Browser', SAMSUNG: 'Samsung Internet', - UC: 'UC Browser', + SEAMONKEY: 'SeaMonkey', + SILK: 'Silk', + SKYFIRE: 'Skyfire', + SLEIPNIR: 'Sleipnir', + SLIMBROWSER: 'SlimBrowser', + SNAPCHAT: 'Snapchat', + SOGOU_EXPLORER: 'Sogou Explorer', + SOGOU_MOBILE: 'Sogou Mobile', + SWIFTFOX: 'Swiftfox', + TESLA: 'Tesla', + TIKTOK: 'TikTok', + TIZEN: 'Tizen Browser', + UC: 'UCBrowser', + UP: 'UP.Browser', + VIERA: 'Viera', VIVALDI: 'Vivaldi', + VIVO: 'Vivo Browser', + W3M: 'w3m', + WATERFOX: 'Waterfox', + WEBKIT: 'WebKit', + WECHAT: 'WeChat', + WEIBO: 'Weibo', + WHALE: 'Whale', YANDEX: 'Yandex' // TODO : test! @@ -41,25 +150,27 @@ const Browser = Object.freeze({ const CPU = Object.freeze({ ARM : 'arm', - ARM64: 'arm64', - ARMHF: 'armhf', + ARM_64: 'arm64', + ARM_HF: 'armhf', AVR: 'avr', + AVR_32: 'avr32', IA64: 'ia64', IRIX: 'irix', - IRIX64: 'irix64', + IRIX_64: 'irix64', MIPS: 'mips', - MIPS64: 'mips64', - MOTO_68K: '68k', + MIPS_64: 'mips64', + M68K: '68k', + PA_RISC: 'pa-risc', PPC: 'ppc', SPARC: 'sparc', - SPARC64: 'sparc64', + SPARC_64: 'sparc64', X86: 'ia32', X86_64: 'amd64' }); const Device = Object.freeze({ CONSOLE: 'console', - DEKSTOP: 'desktop', + DESKTOP: 'desktop', EMBEDDED: 'embedded', MOBILE: 'mobile', SMARTTV: 'smarttv', @@ -114,6 +225,7 @@ const Vendor = Object.freeze({ SIEMENS: 'Siemens', SONY: 'Sony', SPRINT: 'Sprint', + TECHNISAT: 'TechniSAT', TECNO: 'Tecno', TESLA: 'Tesla', ULEFONE: 'Ulefone', @@ -159,14 +271,15 @@ const OS = Object.freeze({ BLACKBERRY: 'BlackBerry', CENTOS: 'CentOS', CHROME_OS: 'Chrome OS', + CHROMECAST: 'Chromecast', CONTIKI: 'Contiki', - FEDORA: 'Fedora', - FIREFOX_OS: 'Firefox OS', - FREEBSD: 'FreeBSD', DEBIAN: 'Debian', DEEPIN: 'Deepin', DRAGONFLY: 'DragonFly', ELEMENTARY_OS: 'elementary OS', + FEDORA: 'Fedora', + FIREFOX_OS: 'Firefox OS', + FREEBSD: 'FreeBSD', FUCHSIA: 'Fuchsia', GENTOO: 'Gentoo', GHOSTBSD: 'GhostBSD', @@ -225,6 +338,7 @@ const OS = Object.freeze({ WINDOWS: 'Windows', WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', + XBOX: 'Xbox', ZENWALK: 'Zenwalk' // TODO : test! diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 12750c804..5f3a5e3fe 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.1 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 4b1d5cd86..777d227ab 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta.1 +/* Extensions for UAParser.js v2.0.0-beta.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 0af69e356..e0f9c8acf 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta.1 +/* Extensions for UAParser.js v2.0.0-beta.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index c7e9b1e07..81023616b 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.1 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 350b02988..17eea7a86 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-beta.1 +/* Helpers for UAParser.js v2.0.0-beta.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs new file mode 100644 index 000000000..d710c2880 --- /dev/null +++ b/src/helpers/ua-parser-helpers.mjs @@ -0,0 +1,26 @@ +// Generated ESM version of ua-parser-js/helpers +// DO NOT EDIT THIS FILE! +// Source: /src/helpers/ua-parser-helpers.js + +/////////////////////////////////////////////// +/* Helpers for UAParser.js v2.0.0-beta.2 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +import { CPU, OS, Engine } from '../enums/ua-parser-enums'; + +const isAppleSilicon = (res) => res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); + +const isChromiumBased = (res) => res.engine.is(Engine.BLINK); + +const isFrozenUA = (ua) => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); + +export { + isAppleSilicon, + isChromiumBased, + isFrozenUA +} \ No newline at end of file diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 3fa574945..09308fe94 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0-beta.1 +// Type definitions for UAParser.js v2.0.0-beta.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index df3c2dab2..7de870be1 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta.1 +/* UAParser.js v2.0.0-beta.2 Copyright © 2012-2023 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-beta.1', + var LIBVERSION = '2.0.0-beta.2', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index abb8d6029..0f4c2a87b 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta.1 +/* UAParser.js v2.0.0-beta.2 Copyright © 2012-2023 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,8 +21,7 @@ // Constants ///////////// - - var LIBVERSION = '2.0.0-beta.1', + var LIBVERSION = '2.0.0-beta.2', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -43,7 +42,7 @@ WEARABLE = 'wearable', EMBEDDED = 'embedded', USER_AGENT = 'user-agent', - UA_MAX_LENGTH = 350, + UA_MAX_LENGTH = 500, BRANDS = 'brands', FORMFACTOR = 'formFactor', FULLVERLIST = 'fullVersionList', @@ -72,6 +71,7 @@ BLACKBERRY = 'BlackBerry', GOOGLE = 'Google', HUAWEI = 'Huawei', + LENOVO = 'Lenovo', LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', @@ -87,9 +87,11 @@ FIREFOX = 'Firefox', OPERA = 'Opera', FACEBOOK = 'Facebook', + SOGOU = 'Sogou', WINDOWS = 'Windows'; - var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ? + var isWindow = typeof window !== UNDEF_TYPE, + NAVIGATOR = (isWindow && window.navigator) ? window.navigator : undefined, NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ? @@ -121,13 +123,16 @@ } return false; } - return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; + return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, isExtensions = function (obj) { for (var prop in obj) { return /^(browser|cpu|device|engine|os)$/.test(prop); } }, + isString = function (val) { + return typeof val === STR_TYPE; + }, itemListToArray = function (header) { if (!header) return undefined; var arr = []; @@ -137,16 +142,16 @@ var token = trim(tokens[i]).split(';v='); arr[i] = { brand : token[0], version : token[1] }; } else { - arr[i] = tokens[i]; + arr[i] = trim(tokens[i]); } } return arr; }, lowerize = function (str) { - return typeof(str) === STR_TYPE ? str.toLowerCase() : str; + return isString(str) ? str.toLowerCase() : str; }, majorize = function (version) { - return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; + return isString(version) ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; }, setProps = function (arr) { for (var i in arr) { @@ -160,15 +165,15 @@ return this; }, strip = function (pattern, str) { - return str.replace(pattern, EMPTY); + return isString(str) ? str.replace(pattern, EMPTY) : str; }, - stripQuotes = function (val) { - return typeof val === STR_TYPE ? strip(/\\?\"/g, val) : val; + stripQuotes = function (str) { + return strip(/\\?\"/g, str); }, trim = function (str, len) { - if (typeof(str) === STR_TYPE) { + if (isString(str)) { str = strip(/^\s\s*/, str); - return typeof(len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); + return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); } }; @@ -297,15 +302,18 @@ ], [NAME, VERSION], [ /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0 ], [VERSION, [NAME, OPERA+' Mini']], [ + /\bop(?:rg)?x\/([\w\.]+)/i // Opera GX + ], [VERSION, [NAME, OPERA+' GX']], [ /\bopr\/([\w\.]+)/i // Opera Webkit ], [VERSION, [NAME, OPERA]], [ // Mixed + /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu + ], [VERSION, [NAME, 'Baidu']], [ /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer // Trident based - /(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser - /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser + /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon @@ -317,8 +325,7 @@ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser ], [VERSION, [NAME, 'UCBrowser']], [ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser - /\bqbcore\/([\w\.]+).+microm/i - ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [ + /\bqbcore\/([\w\.]+).+microm/i, /micromessenger\/([\w\.]+)/i // WeChat ], [VERSION, [NAME, 'WeChat']], [ /konqueror\/([\w\.]+)/i // Konqueror @@ -327,6 +334,8 @@ ], [VERSION, [NAME, 'IE']], [ /ya(?:search)?browser\/([\w\.]+)/i // Yandex ], [VERSION, [NAME, 'Yandex']], [ + /slbrowser\/([\w\.]+)/i // Smart Lenovo Browser + ], [VERSION, [NAME, 'Smart ' + LENOVO + SUFFIX_BROWSER]], [ /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [ /\bfocus\/([\w\.]+)/i // Firefox Focus @@ -345,15 +354,20 @@ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ - /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser + /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser + /samsungbrowser\/([\w\.]+)/i // Samsung Internet + ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon ], [[NAME, /_/g, ' '], VERSION], [ + /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer + ], [VERSION, [NAME, SOGOU + ' Explorer']], [ + /(sogou)mo\w+\/([\d\.]+)/i // Sogou Mobile + ], [[NAME, SOGOU + ' Mobile'], VERSION], [ /(electron)\/([\w\.]+) safari/i, // Electron-based App /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla - /m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser + /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser ], [NAME, VERSION], [ - /(metasr)[\/ ]?([\w\.]+)/i, // SouGouBrowser /(lbbrowser)/i, // LieBao Browser /\[(linkedin)app\]/i // LinkedIn App for iOS & Android ], [NAME], [ @@ -361,10 +375,12 @@ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android ], [[NAME, FACEBOOK], VERSION], [ + /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android + /(alipay)client\/([\w\.]+)/i, // Alipay /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS @@ -496,7 +512,7 @@ /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi - /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|\))/i, // Xiaomi Redmi 'numeric' models + /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad @@ -536,7 +552,7 @@ // Lenovo /(ideatab[-\w ]+)/i, /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i - ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ // Nokia /(?:maemo|nokia).*(n900|lumia \d+)/i, @@ -710,7 +726,7 @@ // MIXED (GENERIC) /////////////////// - /droid .+?; ([^;]+?)(?: bui|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors + /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors ], [MODEL, [TYPE, MOBILE]], [ /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors ], [MODEL, [TYPE, TABLET]], [ @@ -747,12 +763,12 @@ // Windows /microsoft (windows) (vista|xp)/i // Windows (iTunes) ], [NAME, VERSION], [ - /(windows) nt 6\.2; (arm)/i, // Windows RT - /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, // Windows Phone - /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i + /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i // Windows Phone ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ - /(win(?=3|9|n)|win 9x )([nt\d\.]+)/i - ], [[NAME, WINDOWS], [VERSION, strMapper, windowsVersionMap]], [ + /windows nt 6\.2; (arm)/i, // Windows RT + /windows[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i, + /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i + ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ // iOS/macOS /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS @@ -1041,15 +1057,16 @@ switch (this.itemType) { case UA_BROWSER: - var brands = uaCH[FULLVERLIST] || uaCH[BRANDS]; + var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; if (brands) { for (var i in brands) { - var brandName = brands[i].brand, + var brandName = strip(/(Google|Microsoft) /, brands[i].brand || brands[i]), brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) { - this.set(NAME, strip(GOOGLE+' ', brandName)) + if (!/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && !/chromi/i.test(brandName)))) { + this.set(NAME, brandName) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); + prevName = brandName; } } } @@ -1068,6 +1085,11 @@ if (uaCH[MODEL]) { this.set(MODEL, uaCH[MODEL]); } + // Xbox-Specific Detection + if (uaCH[MODEL] == 'Xbox') { + this.set(TYPE, CONSOLE) + .set(VENDOR, MICROSOFT); + } if (uaCH[FORMFACTOR]) { var ff; if (typeof uaCH[FORMFACTOR] !== 'string') { @@ -1089,6 +1111,11 @@ this.set(NAME, osName) .set(VERSION, osVersion); } + // Xbox-Specific Detection + if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') { + this.set(NAME, 'Xbox') + .set(VERSION, undefined); + } break; case UA_RESULT: var data = this.data; @@ -1182,7 +1209,7 @@ ['getResult', createItemFunc(UA_RESULT)], ['getUA', function () { return userAgent; }], ['setUA', function (ua) { - if (typeof ua === STR_TYPE) + if (isString(ua)) userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua; return this; }] diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index bae2f9982..471cdb3b4 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1308,6 +1308,16 @@ "major" : "4" } }, + { + "desc" : "Samsung Internet in Redmi 8A", + "ua" : "Mozilla/5.0 (Linux; Android 10; Redmi 8A) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect" : + { + "name" : "Samsung Internet", + "version" : "23.0", + "major" : "23" + } + }, { "desc" : "SeaMonkey", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1b4pre) Gecko/20090405 SeaMonkey/2.0b1pre", From b29a9a7ffbcb0fd0e99c5a49150c2ddb6fa91102 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 28 Feb 2024 10:19:30 +0700 Subject: [PATCH 180/388] Fix #708 - Improve detection for Quest 3 --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7de870be1..2bdf78641 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -708,7 +708,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ - /(quest( 2| pro)?)/i // Oculus Quest + /(quest( \d| pro)?)/i // Oculus Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ /////////////////// diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 3ccb234da..fbaf6e942 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1385,6 +1385,15 @@ "type": "wearable" } }, + { + "desc": "Oculus Quest 3", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest 3) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/31.4.0.6.51.566757996 Chrome/120.0.6099.283 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest 3", + "type": "wearable" + } + }, { "desc": "Oculus Quest Pro", "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest Pro) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/24.4.0.22.60.426469926 SamsungBrowser/4.0 Chrome/106.0.5249.181 VR Safari/537.36", From a43d6595777b5caa510ddf198497187464f7d5f8 Mon Sep 17 00:00:00 2001 From: Dai Jie Date: Tue, 19 Mar 2024 22:32:43 +0800 Subject: [PATCH 181/388] Fix #710: Add type to IBrowser (#711) --- src/main/ua-parser.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 09308fe94..6ccf8676d 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -15,6 +15,7 @@ declare namespace UAParser { name?: string; version?: string; major?: string; + type?: string; } interface ICPU extends IData { From 8dce4cc5146c70239076b0610a1fe241cbcd9fe6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 17 May 2024 22:36:41 +0700 Subject: [PATCH 182/388] Fix #721 - Improve detection: recognize OPPO Pad as tablet --- src/main/ua-parser.js | 2 ++ test/specs/device-all.json | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 2bdf78641..b00a8715e 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -521,6 +521,8 @@ /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ + /\b(opd2\d{3}a?) bui\//i + ], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [ // Vivo /vivo (\w+)(?: bui|\))/i, diff --git a/test/specs/device-all.json b/test/specs/device-all.json index fbaf6e942..3927c2aa9 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1511,6 +1511,15 @@ "type": "mobile" } }, + { + "desc": "OPPO Pad", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; OPD2101 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OPD2101", + "type": "tablet" + } + }, { "desc": "OPPO Neo", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; R831T Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 OppoBrowser/3.3.2 Mobile Safari/534.30", From d0db40c2906aba9e33d2a19a9e777ab62bc8956a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 17 May 2024 22:56:52 +0700 Subject: [PATCH 183/388] Fix #722 - Add new browser name: Twitter --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 3 ++- test/specs/browser-all.json | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 8930a2712..f7dab4ba1 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -128,6 +128,7 @@ const Browser = Object.freeze({ TESLA: 'Tesla', TIKTOK: 'TikTok', TIZEN: 'Tizen Browser', + TWITTER: 'Twitter', UC: 'UCBrowser', UP: 'UP.Browser', VIERA: 'Viera', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b00a8715e..daa96840d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -379,6 +379,7 @@ /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay + /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS @@ -521,7 +522,7 @@ /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ - /\b(opd2\d{3}a?) bui\//i + /\b(opd2\d{3}a?) bui/i ], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [ // Vivo diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 471cdb3b4..815fcc02d 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -2071,5 +2071,25 @@ "version" : "12.33.0.36", "major" : "12" } + }, + { + "desc" : "Twitter", + "ua" : "Mozilla/5.0 (Linux; Android 13; CPH2531 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.120 Mobile Safari/537.36 TwitterAndroid", + "expect" : + { + "name" : "Twitter", + "version" : "undefined", + "major" : "undefined" + } + }, + { + "desc" : "Twitter", + "ua" : "Mozilla/5.0 (iPad; CPU OS 15_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/19H12 Twitter for iPhone/10.34", + "expect" : + { + "name" : "Twitter", + "version" : "10.34", + "major" : "10" + } } ] \ No newline at end of file From e87c794fd95628743f25027b58faed00dc8d0cdc Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 23 May 2024 18:12:02 +0700 Subject: [PATCH 184/388] Fix #730 - Improve browser detection: DuckDuckGo --- src/main/ua-parser.js | 2 ++ test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index daa96840d..707611f28 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -320,6 +320,8 @@ /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ + /\bddg\/([\w\.]+)/i // DuckDuckGo + ], [VERSION, [NAME, 'DuckDuckGo']], [ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser ], [VERSION, [NAME, 'UCBrowser']], [ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 815fcc02d..229da189f 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -419,6 +419,16 @@ "major" : "1" } }, + { + "desc" : "DuckDuckGo", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.1517.4.1 Ddg/17.4.1", + "expect" : + { + "name" : "DuckDuckGo", + "version" : "17.4.1", + "major" : "17" + } + }, { "desc" : "DuckDuckGo", "ua" : "Mozilla/5.0 (Linux; Android 8.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile DuckDuckGo/5 Safari/537.36", From 150d3c6b4af498ca754099ab34e9426a02c82ab7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 1 Jun 2024 17:52:12 +0700 Subject: [PATCH 185/388] Add new feature: parse user-agent in CLI using `npx ua-parser-js "[INSERT-UA-HERE]"` and print the result in JSON format --- package.json | 1 + script/cli.js | 4 ++++ 2 files changed, 5 insertions(+) create mode 100755 script/cli.js diff --git a/package.json b/package.json index 09bfd49ff..5e067dd25 100755 --- a/package.json +++ b/package.json @@ -196,6 +196,7 @@ "dist", "src" ], + "bin": "./script/cli.js", "scripts": { "build": "./script/build-dist.sh && ./script/build-module.js", "build+test": "npm run build && npm run test", diff --git a/script/cli.js b/script/cli.js new file mode 100755 index 000000000..c015bd3b1 --- /dev/null +++ b/script/cli.js @@ -0,0 +1,4 @@ +#!/usr/bin/env node + +const UAParser = require('ua-parser-js'); +console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)))); \ No newline at end of file From 5a8ce350548142081e60e3e2cdf79fb71206254a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 10:38:24 +0700 Subject: [PATCH 186/388] Insert spaces to command line output for readability --- script/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/cli.js b/script/cli.js index c015bd3b1..785a10426 100755 --- a/script/cli.js +++ b/script/cli.js @@ -1,4 +1,4 @@ #!/usr/bin/env node const UAParser = require('ua-parser-js'); -console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)))); \ No newline at end of file +console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)), null, 4)); \ No newline at end of file From 760e85bbe7b55bfac3e2a78c055897ea3968c62b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:03:27 +0700 Subject: [PATCH 187/388] Update test for some missing browsers: Blazer, Comodo Dragon, Conkeror, Go Browser, Iron, Jasmine, Links, NetSurf, OviBrowser, Quark, Rekonq, w3m --- test/specs/browser-all.json | 174 +++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 2 deletions(-) diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 229da189f..abff8daa2 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -209,6 +209,16 @@ "major" : "11" } }, + { + "desc" : "Blazer", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; PalmSource/hspr-H102; Blazer/4.0) 16;320x320", + "expect" : + { + "name" : "Blazer", + "version" : "4.0", + "major" : "4" + } + }, { "desc" : "Bolt", "ua" : "Mozilla/5.0 (X11; 78; CentOS; US-en) AppleWebKit/527+ (KHTML, like Gecko) Bolt/0.862 Version/3.0 Safari/523.15", @@ -389,6 +399,26 @@ "major" : "78" } }, + { + "desc" : "Comodo Dragon", + "ua" : "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/535.7 (KHTML, like Gecko) Comodo_Dragon/16.1.1.0 Chrome/16.0.912.63 Safari/535.7", + "expect" : + { + "name" : "Comodo Dragon", + "version" : "16.1.1.0", + "major" : "16" + } + }, + { + "desc" : "Conkeror", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:6.0.1) Gecko/20110831 conkeror/0.9.3", + "expect" : + { + "name" : "conkeror", + "version" : "0.9.3", + "major" : "0" + } + }, { "desc" : "Dillo", "ua" : "Dillo/2.2", @@ -459,6 +489,16 @@ "major" : "5" } }, + { + "desc" : "Go Browser", + "ua" : "NokiaE66/GoBrowser/2.0.297", + "expect" : + { + "name" : "GoBrowser", + "version" : "2.0.297", + "major" : "2" + } + }, { "desc" : "Waterfox", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.2.2 Waterfox/55.2.2", @@ -778,6 +818,26 @@ "major" : "11" } }, + { + "desc" : "Iron", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1250.0 Iron/22.0.2150.0 Safari/537.4", + "expect" : + { + "name" : "Iron", + "version" : "22.0.2150.0", + "major" : "22" + } + }, + { + "desc" : "Jasmine", + "ua" : "SAMSUNG-S8000/S8000XXIF3 SHP/VPP/R5 Jasmine/1.0 Nextreaming SMM-MMS/1.2.0 profile/MIDP-2.1 configuration/CLDC-1.1", + "expect" : + { + "name" : "Jasmine", + "version" : "1.0", + "major" : "1" + } + }, { "desc" : "K-Meleon", "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031016 K-Meleon/0.8.2", @@ -1018,6 +1078,26 @@ "major" : "6" } }, + { + "desc" : "NetSurf in Plan9", + "ua" : "Mozilla/5.0 (Plan9) NetSurf/3.12", + "expect" : + { + "name" : "NetSurf", + "version" : "3.12", + "major" : "3" + } + }, + { + "desc" : "NetSurf in Linux", + "ua" : "NetSurf/3.10 (Linux; Arch Linux)", + "expect" : + { + "name" : "NetSurf", + "version" : "3.10", + "major" : "3" + } + }, { "desc" : "Nokia Browser", "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaN8-00/025.007; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.37 Mobile Safari/533.4 3gpp-gba", @@ -1198,6 +1278,16 @@ "major" : "1" } }, + { + "desc" : "OviBrowser", + "ua" : "Mozilla/5.0 (Series40; NokiaX3-02/le6.32; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/1.0.0.11.8", + "expect" : + { + "name" : "OviBrowser", + "version" : "1.0.0.11.8", + "major" : "1" + } + }, { "desc" : "PhantomJS", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.2 Safari/534.34", @@ -1229,7 +1319,7 @@ } }, { - "desc" : "QQ", + "desc" : "QQBrowser", "ua" : "Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; OPPO R7s Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/7.1 Mobile Safari/537.36", "expect" : { @@ -1238,6 +1328,26 @@ "major" : "7" } }, + { + "desc" : "QQBrowser", + "ua" : "Mozilla/5.0 (Linux; U; Android 9; zh-cn; vivo X21 Build/PKQ1.180819.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.9 Mobile Safari/537.36", + "expect" : + { + "name" : "QQBrowser", + "version" : "9.9", + "major" : "9" + } + }, + { + "desc" : "Quark", + "ua" : "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; JLH-AN00 Build/HONORJLH-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/5.8.2.221 Mobile Safari/537.36", + "expect" : + { + "name" : "Quark", + "version" : "5.8.2.221", + "major" : "5" + } + }, { "desc" : "QupZilla", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.8.9 Safari/538.1", @@ -1248,6 +1358,16 @@ "major" : "1" } }, + { + "desc" : "Rekonq 2", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) rekonq/2.2.1 Safari/537.21", + "expect" : + { + "name" : "rekonq", + "version" : "2.2.1", + "major" : "2" + } + }, { "desc" : "RockMelt", "ua" : "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) RockMelt/0.8.36.78 Chrome/7.0.517.44 Safari/534.7", @@ -1479,7 +1599,7 @@ } }, { - "desc" : "UPBrowser", + "desc" : "UP.Browser", "ua" : "BenQ-CF61/1.00/WAP2.0/MIDP2.0/CLDC1.0 UP.Browser/6.3.0.4.c.1.102 (GUI) MMP/2.0", "expect" : { @@ -1558,6 +1678,16 @@ "major" : "undefined" } }, + { + "desc" : "w3m", + "ua" : "w3m/0.5.1", + "expect" : + { + "name" : "w3m", + "version" : "0.5.1", + "major" : "0" + } + }, { "desc" : "Yandex", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5402 Safari/536.5", @@ -1914,6 +2044,46 @@ "name" : "LinkedIn" } }, + { + "desc" : "Links in Linux", + "ua" : "Links (2.xpre7; Linux 2.4.18 i586; x)", + "expect" : + { + "name" : "Links", + "version" : "2.xpre7", + "major" : "2" + } + }, + { + "desc" : "Links in Mac", + "ua" : "Links (2.1pre33; Darwin 8.11.0 Power Macintosh; 169x55)", + "expect" : + { + "name" : "Links", + "version" : "2.1pre33", + "major" : "2" + } + }, + { + "desc" : "Links in NetBSD", + "ua" : "Links (2.29; NetBSD 10.0 i386; GNU C 10.5; x)", + "expect" : + { + "name" : "Links", + "version" : "2.29", + "major" : "2" + } + }, + { + "desc" : "Links in FreeBSD", + "ua" : "Links (2.1pre15; FreeBSD 5.3-RELEASE i386; 196x84)", + "expect" : + { + "name" : "Links", + "version" : "2.1pre15", + "major" : "2" + } + }, { "desc" : "Safari including comma in minor version number", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6,2 Safari/605.1.15", From 6b6fcc68f50514626e224d511242534140b84acb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:10:37 +0700 Subject: [PATCH 188/388] Improve browser detection for Sleipnir --- src/main/ua-parser.js | 7 ++++--- test/specs/browser-all.json | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 707611f28..7b4df95f4 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -309,7 +309,8 @@ /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu ], [VERSION, [NAME, 'Baidu']], [ /(kindle)\/([\w\.]+)/i, // Kindle - /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer + /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, + // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir // Trident based /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer @@ -433,8 +434,8 @@ /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla // Other - /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, - // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser + /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser /(links) \(([\w\.]+)/i, // Links /panasonic;(viera)/i // Panasonic Viera ], [NAME, VERSION], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index abff8daa2..fd0f18317 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1487,6 +1487,37 @@ "version" : "2.0", "major" : "2" } + }, + { + "desc" : "Sleipnir", + "ua" : "Mozilla/5.0 (Linux; Android 10; SOV37 Build/52.1.C.0.220; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.120 Mobile Safari/537.36 Sleipnir/3.7.5", + "expect" : + { + "name" : "Sleipnir", + "version" : "3.7.5", + "major" : "3" + } + }, + + { + "desc" : "Sleipnir", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Sleipnir 2.8.4)", + "expect" : + { + "name" : "Sleipnir", + "version" : "2.8.4", + "major" : "2" + } + }, + { + "desc" : "Sleipnir", + "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) Sleipnir/2.8.4", + "expect" : + { + "name" : "Sleipnir", + "version" : "2.8.4", + "major" : "2" + } }, { "desc" : "SlimBrowser", From 4cd867a36e78391581e771573737276b858348ca Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:14:42 +0700 Subject: [PATCH 189/388] Improve browser detection for Klar https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox#klar_for_android --- src/main/ua-parser.js | 8 ++++---- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7b4df95f4..fae8f2d35 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -316,8 +316,8 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo|klar)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ @@ -426,8 +426,8 @@ ], [VERSION, [NAME, FIREFOX+' Reality']], [ /ekiohf.+(flow)\/([\w\.]+)/i, // Flow /(swiftfox)/i, // Swiftfox - /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, - // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar + /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i, + // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix /(firefox)\/([\w\.]+)/i, // Other Firefox-based diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index fd0f18317..972951c95 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -858,6 +858,16 @@ "major" : "2" } }, + { + "desc" : "Klar < 4.1", + "ua" : "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Klar/1.0 Chrome/58.0.3029.83 Mobile Safari/537.36", + "expect" : + { + "name" : "Klar", + "version" : "1.0", + "major" : "1" + } + }, { "desc" : "Konqueror", "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux; X11; x86_64) KHTML/3.5.6 (like Gecko) (Kubuntu)", From 1fa3d02594f46ae5c4e96a5b3b6508cc4d9246e0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:20:50 +0700 Subject: [PATCH 190/388] Remove Viera from list of browsers --- src/enums/ua-parser-enums.js | 3 +-- src/main/ua-parser.js | 1 - test/specs/browser-all.json | 10 ---------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index f7dab4ba1..c0706779b 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -50,7 +50,7 @@ const Browser = Object.freeze({ FENNEC: 'Fennec', FLOCK: 'Flock', FLOW: 'Flow', - GO: 'Go Browser', + GO: 'GoBrowser', GOOGLE_SEARCH: 'GSA', HEYTAP: 'HeyTap', HUAWEI: 'Huawei Browser', @@ -131,7 +131,6 @@ const Browser = Object.freeze({ TWITTER: 'Twitter', UC: 'UCBrowser', UP: 'UP.Browser', - VIERA: 'Viera', VIVALDI: 'Vivaldi', VIVO: 'Vivo Browser', W3M: 'w3m', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index fae8f2d35..80ffc962b 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -437,7 +437,6 @@ /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser /(links) \(([\w\.]+)/i, // Links - /panasonic;(viera)/i // Panasonic Viera ], [NAME, VERSION], [ /(cobalt)\/([\w\.]+)/i // Cobalt diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 972951c95..191dea943 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1709,16 +1709,6 @@ "major" : "16" } }, - { - "desc" : "Viera", - "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", - "expect" : - { - "name" : "VIERA", - "version" : "undefined", - "major" : "undefined" - } - }, { "desc" : "w3m", "ua" : "w3m/0.5.1", From 85bf7076d3b2f189c7a59586e0832f4b97f8ccaf Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:32:23 +0700 Subject: [PATCH 191/388] Improve browser detection for ICEBrowser --- src/main/ua-parser.js | 4 ++-- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 80ffc962b..bc66b7aaf 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -436,8 +436,8 @@ // Other /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i, // Links - ], [NAME, VERSION], [ + /(links) \(([\w\.]+)/i // Links + ], [NAME, [VERSION, /_/g, '.']], [ /(cobalt)\/([\w\.]+)/i // Cobalt ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 191dea943..db9b84928 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -758,6 +758,16 @@ "major" : "2" } }, + { + "desc" : "ICEBrowser", + "ua" : "Mozilla/5.0 (Java 1.6.0_01; Windows XP 5.1 x86; en) ICEbrowser/v6_1_2", + "expect" : + { + "name" : "ICEbrowser", + "version" : "6.1.2", + "major" : "6" + } + }, { "desc" : "IceCat", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092921 IceCat/3.0.3-g1", From 12c2c2e48adf7d271b89213768c224b1b776e89f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:35:33 +0700 Subject: [PATCH 192/388] Improve browser detection for Rekonq --- src/main/ua-parser.js | 2 +- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index bc66b7aaf..7f4c3ff5d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -369,7 +369,7 @@ /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser ], [NAME, VERSION], [ - /(lbbrowser)/i, // LieBao Browser + /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq /\[(linkedin)app\]/i // LinkedIn App for iOS & Android ], [NAME], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index db9b84928..d90c4c5d4 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -898,6 +898,16 @@ "major" : "5" } }, + { + "desc" : "Rekonq", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ) AppleWebKit/533.3 (KHTML, like Gecko) rekonq Safari/533.3", + "expect" : + { + "name" : "rekonq", + "version" : "undefined", + "major" : "undefined" + } + }, { "desc" : "Smart Lenovo Browser", "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.0.10171 SLBChan/8", From 1a2ef00509bd72ca18854baeb64dbdc64a8bc64d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 15:49:27 +0700 Subject: [PATCH 193/388] Improve browser detection for QQBrowser --- src/main/ua-parser.js | 4 +++- test/specs/browser-all.json | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7f4c3ff5d..b0b3de04e 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -316,7 +316,7 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo|klar)\/([-\w\.]+)/i, + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo @@ -355,6 +355,8 @@ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ + /\b(qq)\/([\w\.]+)/i // QQ + ], [[NAME, /(.+)/, '$1Browser'], VERSION], [ /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser /samsungbrowser\/([\w\.]+)/i // Samsung Internet diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index d90c4c5d4..77aca1b16 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1944,7 +1944,7 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Mobile/14A456 QQ/6.5.3.410 V1_IPH_SQ_6.5.3_1_APP_A Pixel/1080 Core/UIWebView NetType/WIFI Mem/26", "expect" : { - "name" : "QQ", + "name" : "QQBrowser", "version" : "6.5.3.410", "major" : "6" } @@ -1954,7 +1954,7 @@ "ua" : "Mozilla/5.0 (Linux; Android 6.0; PRO 6 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile MQQBrowser/6.8 TBS/036824 Safari/537.36 V1_AND_SQ_6.5.8_422_YYB_D PA QQ/6.5.8.2910 NetType/WIFI WebP/0.3.0 Pixel/1080", "expect" : { - "name" : "QQ", + "name" : "QQBrowser", "version" : "6.5.8.2910", "major" : "6" } From 8991d34e56630bb7d0daf294bba942a1a262733f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 21:21:53 +0700 Subject: [PATCH 194/388] Update `formFactor` -> `formFactors`, in accordance to the latest change in client hints spec --- src/main/ua-parser.js | 20 ++++++++++---------- test/mocha-test.js | 8 ++++---- test/playwright-test-main.spec.mjs | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b0b3de04e..693ee88fd 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -42,7 +42,7 @@ USER_AGENT = 'user-agent', UA_MAX_LENGTH = 500, BRANDS = 'brands', - FORMFACTOR = 'formFactor', + FORMFACTORS = 'formFactors', FULLVERLIST = 'fullVersionList', PLATFORM = 'platform', PLATFORMVER = 'platformVersion', @@ -51,12 +51,12 @@ CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, - CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor', + CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors', CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS], + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', @@ -269,7 +269,7 @@ 'RT' : 'ARM' }, - formFactorMap = { + formFactorsMap = { 'embedded' : 'Automotive', 'mobile' : 'Mobile', 'tablet' : ['Tablet', 'EInk'], @@ -975,7 +975,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], - [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], + [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1095,15 +1095,15 @@ this.set(TYPE, CONSOLE) .set(VENDOR, MICROSOFT); } - if (uaCH[FORMFACTOR]) { + if (uaCH[FORMFACTORS]) { var ff; - if (typeof uaCH[FORMFACTOR] !== 'string') { + if (typeof uaCH[FORMFACTORS] !== 'string') { var idx = 0; - while (!ff && idx < uaCH[FORMFACTOR].length) { - ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); + while (!ff && idx < uaCH[FORMFACTORS].length) { + ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap); } } else { - ff = strMapper(uaCH[FORMFACTOR], formFactorMap); + ff = strMapper(uaCH[FORMFACTORS], formFactorsMap); } this.set(TYPE, ff); } diff --git a/test/mocha-test.js b/test/mocha-test.js index 0de0efe1d..624138370 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -463,18 +463,18 @@ describe('Map UA-CH headers', function () { }); }); - it('Can detect form-factor from client-hints', function () { + it('Can detect form-factors from client-hints', function () { const FFVR = { - 'sec-ch-ua-form-factor' : '"VR"' + 'sec-ch-ua-form-factors' : '"VR"' }; const FFEInk = { - 'sec-ch-ua-form-factor' : '"Tablet", "EInk"' + 'sec-ch-ua-form-factors' : '"Tablet", "EInk"' }; const FFUnknown = { - 'sec-ch-ua-form-factor' : '"Unknown"' + 'sec-ch-ua-form-factors' : '"Unknown"' }; UAParser(FFVR).withClientHints().then(function (ua) { diff --git a/test/playwright-test-main.spec.mjs b/test/playwright-test-main.spec.mjs index 982e9429f..e830bbfeb 100644 --- a/test/playwright-test-main.spec.mjs +++ b/test/playwright-test-main.spec.mjs @@ -40,7 +40,7 @@ test('read client hints data', async ({ page }) => { } ], platform: 'New OS', - formFactor: 'New Form Factor' + formFactors: 'New Form Factor' }); } } From 1a22c6951f725a16af7bc2c99fc60f2c9e2d6869 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 22:28:58 +0700 Subject: [PATCH 195/388] Update all package references in /test to use current working directories --- test/jazzer-fuzz-test.js | 2 +- test/mocha-test-es6.mjs | 4 ++-- test/mocha-test-extension.js | 4 ++-- test/mocha-test-helpers.js | 4 ++-- test/mocha-test.js | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/jazzer-fuzz-test.js b/test/jazzer-fuzz-test.js index ee30cf26e..8d8704952 100644 --- a/test/jazzer-fuzz-test.js +++ b/test/jazzer-fuzz-test.js @@ -1,5 +1,5 @@ const { FuzzedDataProvider } = require('@jazzer.js/core'); -const UAParser = require('ua-parser-js'); +const { UAParser } = require('../src/main/ua-parser'); const UA_MAX_LENGTH = 350; module.exports.fuzz = function (buffer) { diff --git a/test/mocha-test-es6.mjs b/test/mocha-test-es6.mjs index 54c4dcad3..c0bd217f7 100644 --- a/test/mocha-test-es6.mjs +++ b/test/mocha-test-es6.mjs @@ -1,5 +1,5 @@ -import { UAParser } from 'ua-parser-js'; -import { CPU, Device, Engine } from 'ua-parser-js/enums'; +import { UAParser } from '../src/main/ua-parser.mjs'; +import { CPU, Device, Engine } from '../src/enums/ua-parser-enums.mjs'; import * as assert from 'assert'; describe('Returns', () => { diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index fd29fd8a4..45418ac4b 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -3,8 +3,8 @@ const assert = require('assert'); const parseJS = require('@babel/parser').parse; const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); -const UAParser = require('ua-parser-js'); -const { Bots, CLIs, Emails, Modules } = require('ua-parser-js/extensions'); +const { UAParser } = require('../src/main/ua-parser'); +const { Bots, CLIs, Emails, Modules } = require('../src/extensions/ua-parser-extensions'); describe('Bots', () => { it('Can detect bots', () => { diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index 9e27d8985..faacf617c 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,6 +1,6 @@ const assert = require('assert'); -const UAParser = require('ua-parser-js'); -const { isAppleSilicon, isChromiumBased } = require('ua-parser-js/helpers'); +const { UAParser } = require('../src/main/ua-parser'); +const { isAppleSilicon, isChromiumBased } = require('../src/helpers/ua-parser-helpers'); describe('isAppleSilicon', () => { it('Can detect Apple Silicon device', () => { diff --git a/test/mocha-test.js b/test/mocha-test.js index 624138370..2de9d8736 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -4,7 +4,7 @@ var assert = require('assert'); var requirejs = require('requirejs'); var parseJS = require('@babel/parser').parse; var traverse = require('@babel/traverse').default; -var UAParser = require('ua-parser-js'); +var {UAParser} = require('../src/main/ua-parser'); var browsers = require('./specs/browser-all.json'); var cpus = require('./specs/cpu-all.json'); var devices = require('./specs/device-all.json'); From 39590f112dd96c3b173caaa889f407a510d72200 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 2 Jun 2024 23:04:25 +0700 Subject: [PATCH 196/388] BREAKING CHANGE - Add new property to `browser`: `type` --- src/enums/ua-parser-enums.js | 11 ++++++++++- src/extensions/ua-parser-extensions.js | 14 +++++++------- src/main/ua-parser.d.ts | 1 + src/main/ua-parser.js | 4 ++-- test/dts-test.ts | 1 + test/mocha-test-es6.mjs | 3 +-- test/mocha-test-extension.js | 2 +- test/mocha-test.js | 3 ++- 8 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index c0706779b..692fe0ebe 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -144,6 +144,14 @@ const Browser = Object.freeze({ // TODO : test! }); +const BrowserType = Object.freeze({ + BOT: 'bot', + CLI: 'cli', + EMAIL: 'email', + INAPP: 'inapp', + MODULE: 'module' +}); + const CPU = Object.freeze({ ARM : 'arm', ARM_64: 'arm64', @@ -341,7 +349,8 @@ const OS = Object.freeze({ }); module.exports = { - Browser, + Browser, + BrowserType, CPU, Device, Vendor, diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 777d227ab..9d7387804 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -15,12 +15,6 @@ const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; -const Apps = Object.freeze({ - browser : [ - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']] - ] -}); - const Bots = Object.freeze({ browser : [ // Googlebot / BingBot / MSNBot / FacebookBot @@ -124,6 +118,12 @@ const Emails = Object.freeze({ ] }); +const InApps = Object.freeze({ + browser : [ + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'inapp']] + ] +}); + const MediaPlayers = Object.freeze({ browser : [[ @@ -238,11 +238,11 @@ const Modules = Object.freeze({ }); module.exports = { - Apps, Bots, CLIs, ExtraDevices, Emails, + InApps, MediaPlayers, Modules }; \ No newline at end of file diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 6ccf8676d..b37c67cdd 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -62,6 +62,7 @@ declare namespace UAParser { NAME: 'name'; VERSION: 'version'; MAJOR: 'major'; + TYPE: 'type'; }; static readonly CPU: { ARCHITECTURE: 'architecture'; diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 693ee88fd..221775950 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -847,7 +847,7 @@ var defaultProps = (function () { var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; setProps.call(props.init, [ - [UA_BROWSER, [NAME, VERSION, MAJOR]], + [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]], [UA_CPU, [ARCHITECTURE]], [UA_DEVICE, [TYPE, MODEL, VENDOR]], [UA_ENGINE, [NAME, VERSION]], @@ -1225,7 +1225,7 @@ } UAParser.VERSION = LIBVERSION; - UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); + UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]); UAParser.CPU = enumerize([ARCHITECTURE]); UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); diff --git a/test/dts-test.ts b/test/dts-test.ts index 6ede7002b..f181c6b24 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -28,6 +28,7 @@ expectType(browser); expectType(browser.name); expectType(browser.version); expectType(browser.major); +expectType(browser.type); expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); diff --git a/test/mocha-test-es6.mjs b/test/mocha-test-es6.mjs index c0bd217f7..69a334e6b 100644 --- a/test/mocha-test-es6.mjs +++ b/test/mocha-test-es6.mjs @@ -7,8 +7,7 @@ describe('Returns', () => { assert.deepEqual(new UAParser('').getResult(), { ua : '', - //ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, - browser: { name: undefined, version: undefined, major: undefined }, + browser: { name: undefined, version: undefined, major: undefined, type: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, engine: { name: undefined, version: undefined}, diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 45418ac4b..939802f78 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -26,7 +26,7 @@ describe('Bots', () => { assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"}); + assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8", type: undefined}); // try merging Bots & CLIs const botsAndCLIs = { browser : [...Bots.browser, ...CLIs.browser]}; diff --git a/test/mocha-test.js b/test/mocha-test.js index 2de9d8736..184a84773 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -10,6 +10,7 @@ var cpus = require('./specs/cpu-all.json'); var devices = require('./specs/device-all.json'); var engines = require('./specs/engine-all.json'); var os = require('./specs/os-all.json'); + var parser = new UAParser(); var methods = [ { @@ -82,7 +83,7 @@ describe('Returns', function () { assert.deepEqual(new UAParser('').getResult(), { ua : '', - browser: { name: undefined, version: undefined, major: undefined }, + browser: { name: undefined, version: undefined, major: undefined, type: undefined }, cpu: { architecture: undefined }, device: { vendor: undefined, model: undefined, type: undefined }, engine: { name: undefined, version: undefined}, From 0543b87c02de69f55dfbbc86b619b8448749bf81 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 5 Jun 2024 15:47:27 +0700 Subject: [PATCH 197/388] BREAKING CHANGE: AR/VR devices moved to new device type: `xr` --- src/enums/ua-parser-enums.js | 3 ++- src/main/ua-parser.d.ts | 3 ++- src/main/ua-parser.js | 15 +++++++++++---- test/mocha-test.js | 2 +- test/specs/device-all.json | 8 ++++---- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 692fe0ebe..d994a381a 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -179,7 +179,8 @@ const Device = Object.freeze({ MOBILE: 'mobile', SMARTTV: 'smarttv', TABLET: 'tablet', - WEARABLE: 'wearable' + WEARABLE: 'wearable', + XR: 'xr' }); const Vendor = Object.freeze({ diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index b37c67cdd..9f06b21b1 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -23,7 +23,7 @@ declare namespace UAParser { } interface IDevice extends IData { - type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable'; + type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable' | 'xr' | 'embedded'; vendor?: string; model?: string; } @@ -76,6 +76,7 @@ declare namespace UAParser { SMARTTV: 'smarttv'; TABLET: 'tablet'; WEARABLE: 'wearable'; + XR: 'xr'; EMBEDDED: 'embedded'; }; static readonly ENGINE: { diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 221775950..26038e1b2 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -38,6 +38,7 @@ TABLET = 'tablet', SMARTTV = 'smarttv', WEARABLE = 'wearable', + XR = 'xr', EMBEDDED = 'embedded', USER_AGENT = 'user-agent', UA_MAX_LENGTH = 500, @@ -274,7 +275,8 @@ 'mobile' : 'Mobile', 'tablet' : ['Tablet', 'EInk'], 'smarttv' : 'TV', - 'wearable' : ['VR', 'XR', 'Watch'], + 'wearable' : 'Watch', + 'xr' : ['VR', 'XR'], '?' : ['Desktop', 'Unknown'], '*' : undefined }; @@ -711,12 +713,17 @@ ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ - /droid.+; (glass) \d/i // Google Glass - ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ + + /////////////////// + // XR + /////////////////// + + /droid.+; (glass) \d/i // Google Glass + ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ /(quest( \d| pro)?)/i // Oculus Quest - ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ /////////////////// // EMBEDDED diff --git a/test/mocha-test.js b/test/mocha-test.js index 184a84773..ead59a8bc 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -479,7 +479,7 @@ describe('Map UA-CH headers', function () { }; UAParser(FFVR).withClientHints().then(function (ua) { - assert.strictEqual(ua.device.type, 'wearable'); + assert.strictEqual(ua.device.type, 'xr'); }); UAParser(FFEInk).withClientHints().then(function (ua) { diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 3927c2aa9..252782cdb 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1373,7 +1373,7 @@ "expect": { "vendor": "Facebook", "model": "Quest", - "type": "wearable" + "type": "xr" } }, { @@ -1382,7 +1382,7 @@ "expect": { "vendor": "Facebook", "model": "Quest 2", - "type": "wearable" + "type": "xr" } }, { @@ -1391,7 +1391,7 @@ "expect": { "vendor": "Facebook", "model": "Quest 3", - "type": "wearable" + "type": "xr" } }, { @@ -1400,7 +1400,7 @@ "expect": { "vendor": "Facebook", "model": "Quest Pro", - "type": "wearable" + "type": "xr" } }, { From f7810dbfcfda55784180e1946987f8741cb24fcf Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Jun 2024 14:27:03 +0700 Subject: [PATCH 198/388] Add new browsers: Wolvic & Pico Browser --- src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 6 ++++-- test/specs/browser-all.json | 30 ++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index d994a381a..27afb2ede 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -104,6 +104,7 @@ const Browser = Object.freeze({ PALEMOON: 'PaleMoon', PHANTOMJS: 'PhantomJS', PHOENIX: 'Phoenix', + PICOBROWSER: 'Pico Browser', POLARIS: 'Polaris', PUFFIN: 'Puffin', QQ: 'QQBrowser', @@ -139,6 +140,7 @@ const Browser = Object.freeze({ WECHAT: 'WeChat', WEIBO: 'Weibo', WHALE: 'Whale', + WOLVIC: 'Wolvic', YANDEX: 'Yandex' // TODO : test! diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 26038e1b2..d97570ad8 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -359,8 +359,8 @@ ], [[NAME, '360' + SUFFIX_BROWSER]], [ /\b(qq)\/([\w\.]+)/i // QQ ], [[NAME, /(.+)/, '$1Browser'], VERSION], [ - /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser + /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser /samsungbrowser\/([\w\.]+)/i // Samsung Internet ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon @@ -426,6 +426,8 @@ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ + /(wolvic)\/([\w\.]+)/i // Wolvic + ], [NAME, VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality ], [VERSION, [NAME, FIREFOX+' Reality']], [ /ekiohf.+(flow)\/([\w\.]+)/i, // Flow diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 77aca1b16..c7b3f6c59 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -898,6 +898,26 @@ "major" : "5" } }, + { + "desc" : "PicoBrowser", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", + "expect" : + { + "name" : "PicoBrowser", + "version" : "3.3.22", + "major" : "3" + } + }, + { + "desc" : "PicoBrowser", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect" : + { + "name" : "PicoBrowser", + "version" : "3.3.22", + "major" : "3" + } + }, { "desc" : "Rekonq", "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ) AppleWebKit/533.3 (KHTML, like Gecko) rekonq Safari/533.3", @@ -1739,6 +1759,16 @@ "major" : "0" } }, + { + "desc" : "Wolvic", + "ua" : "Mozilla/5.0 (Android 12; Mobile VR; rv:121.0) Gecko/121.0 Firefox/121.0 Wolvic/1.6.1", + "expect" : + { + "name" : "Wolvic", + "version" : "1.6.1", + "major" : "1" + } + }, { "desc" : "Yandex", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5402 Safari/536.5", From 0a46ac396a351ce168d5e459e0daf9671b6a2035 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Jun 2024 20:02:22 +0700 Subject: [PATCH 199/388] Fix #718 - Extension param now accept multiple extensions --- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 27 ++++++++++++++++++--------- test/mocha-test-extension.js | 6 ++++++ test/mocha-test.js | 7 +++++++ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 9f06b21b1..7af0dfdeb 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -49,7 +49,7 @@ declare namespace UAParser { type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os'; - type UAParserExt = Partial>; + type UAParserExt = Partial> | Partial>[]; export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record): IResult; export function UAParser(uastring?: string, headers?: Record): IResult; diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d97570ad8..eda9ba451 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -101,12 +101,21 @@ // Helper ////////// - var extend = function (regexes, extensions) { - var mergedRegexes = {}; - for (var i in regexes) { - mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; + var extend = function (defaultRgx, extensions) { + var mergedRgx = {}; + var extraRgx = extensions; + if (!isExtensions(extensions)) { + extraRgx = {}; + for (var i in extensions) { + for (var j in extensions[i]) { + extraRgx[j] = extensions[i][j].concat(extraRgx[j] ? extraRgx[j] : []); + } + } + } + for (var k in defaultRgx) { + mergedRgx[k] = extraRgx[k] && extraRgx[k].length % 2 === 0 ? extraRgx[k].concat(defaultRgx[k]) : defaultRgx[k]; } - return mergedRegexes; + return mergedRgx; }, enumerize = function (arr) { var enums = {}; @@ -124,9 +133,9 @@ } return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, - isExtensions = function (obj) { + isExtensions = function (obj, deep) { for (var prop in obj) { - return /^(browser|cpu|device|engine|os)$/.test(prop); + return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false); } }, isString = function (val) { @@ -1163,7 +1172,7 @@ function UAParser (ua, extensions, headers) { if (typeof ua === OBJ_TYPE) { - if (isExtensions(ua)) { + if (isExtensions(ua, true)) { if (typeof extensions === OBJ_TYPE) { headers = extensions; // case UAParser(extensions, headers) } @@ -1173,7 +1182,7 @@ extensions = undefined; } ua = undefined; - } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { + } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) { headers = extensions; // case UAParser(ua, headers) extensions = undefined; } diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 939802f78..6c665025a 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -34,6 +34,12 @@ describe('Bots', () => { assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); + // alternative merge options + const botsAndCLIsParser2 = new UAParser([Bots, CLIs]); + const botsAndCLIsParser3 = new UAParser(facebookBot, [Bots, CLIs]); + assert.deepEqual(botsAndCLIsParser2.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); + assert.deepEqual(botsAndCLIsParser3.getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); + const emailParser = new UAParser(Emails); assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); diff --git a/test/mocha-test.js b/test/mocha-test.js index ead59a8bc..7b5e71173 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -126,6 +126,13 @@ describe('Extending Regex', function () { }); let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; assert.deepEqual(myParser2.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}); + + let myParser3 = new UAParser([{ + browser: myOwnListOfBrowsers + }, { + device: myOwnListOfDevices + }]); + assert.deepEqual(myParser3.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}); }); describe('User-agent length', function () { From 5190905df8c14badcd229541cbe3302b61b90779 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Jun 2024 21:52:16 +0700 Subject: [PATCH 200/388] Clean up & few changes related to browser.type --- src/extensions/ua-parser-extensions.d.ts | 2 +- src/main/ua-parser.d.ts | 2 +- test/dts-test.ts | 2 +- test/specs/browser-all.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 5f3a5e3fe..4be4e3011 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -4,10 +4,10 @@ import type { UAParserExt } from "../main/ua-parser"; -export const Apps: UAParserExt; export const Bots: UAParserExt; export const CLIs: UAParserExt; export const ExtraDevices: UAParserExt; export const Emails: UAParserExt; +export const InApps: UAParserExt; export const MediaPlayers: UAParserExt; export const Modules: UAParserExt; \ No newline at end of file diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 7af0dfdeb..2a44d93c6 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -15,7 +15,7 @@ declare namespace UAParser { name?: string; version?: string; major?: string; - type?: string; + type?: 'bot' | 'cli' | 'email' | 'inapp' | 'module'; } interface ICPU extends IData { diff --git a/test/dts-test.ts b/test/dts-test.ts index f181c6b24..a94349007 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -28,7 +28,7 @@ expectType(browser); expectType(browser.name); expectType(browser.version); expectType(browser.major); -expectType(browser.type); +expectType<'bot' | 'cli' | 'email' | 'inapp' | 'module' | undefined>(browser.type); expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index c7b3f6c59..a9b22df50 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -903,7 +903,7 @@ "ua" : "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", "expect" : { - "name" : "PicoBrowser", + "name" : "Pico Browser", "version" : "3.3.22", "major" : "3" } @@ -913,7 +913,7 @@ "ua" : "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", "expect" : { - "name" : "PicoBrowser", + "name" : "Pico Browser", "version" : "3.3.22", "major" : "3" } From 173325faa182777e212c8f5a9ae087a7df346f86 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Jun 2024 22:36:15 +0700 Subject: [PATCH 201/388] Add some well-known bot user-agents: Applebot, Amazonbot, Bytespider, Claudebot, Yandexbot --- src/extensions/ua-parser-extensions.js | 28 ++++++++++++++++++-------- test/mocha-test-extension.js | 16 +++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 9d7387804..8e416c13c 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -14,24 +14,36 @@ const VENDOR = 'vendor'; const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; +const BOT = 'bot'; +const CLI = 'cli'; +const EMAIL = 'email'; +const INAPP = 'inapp'; +const MODULE = 'module'; const Bots = Object.freeze({ browser : [ // Googlebot / BingBot / MSNBot / FacebookBot - [/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], - // GPTBot - https://platform.openai.com/docs/gptbot - [/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], + // YandexBot - https://yandex.com/bots + // Applebot - http://apple.com/go/applebot + // Amazonbot - https://developer.amazon.com/amazonbot + [/((?:google|bing|msn|facebook|gpt|yandex|apple|amazon)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, BOT]], // Slackbot - https://api.slack.com/robots - [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] + [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, BOT]], + + // ClaudeBot / Bytespider + [/(claude(?:bot|-web)|bytespider)\/?([\w\.]*)/i], [NAME, VERSION, [TYPE, BOT]], + + // Yandex Bots - https://yandex.com/bots + [/http:\/\/(yandex).com\/(bot)s/i], [NAME, TYPE] ] }); const CLIs = Object.freeze({ browser : [ // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] + [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); @@ -114,13 +126,13 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ // Microsoft Outlook / Thunderbird - [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] + [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] ] }); const InApps = Object.freeze({ browser : [ - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'inapp']] + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] ] }); @@ -233,7 +245,7 @@ const MediaPlayers = Object.freeze({ const Modules = Object.freeze({ browser : [ // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']] + [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]] ] }); diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 6c665025a..ca2251b57 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -15,6 +15,14 @@ describe('Bots', () => { const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)'; const wget = 'Wget/1.21.1'; const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)'; + const yandexBot = 'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)'; + const yandexMobileScreenShotBot ='Mozilla/5.0 (compatible; YandexMobileScreenShotBot/1.0; +http://yandex.com/bots)'; + const appleBot = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)'; + const amazonBot = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML\, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)'; + const claudeBot = 'ClaudeBot'; + const claudeBot2 = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)'; + const claudeWeb = 'Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)'; + const bytespider = 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider'; const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; const axios = 'axios/1.3.5'; @@ -25,6 +33,14 @@ describe('Bots', () => { assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(yandexBot).getBrowser(), {name: "YandexBot", version: "3.0", major: "3", type: "bot"}); + assert.deepEqual(botParser.setUA(yandexMobileScreenShotBot).getBrowser(), {name: "yandex", version: undefined, major: undefined, type: "bot"}); + assert.deepEqual(botParser.setUA(appleBot).getBrowser(), {name: "Applebot", version: "0.1", major: "0", type: "bot"}); + assert.deepEqual(botParser.setUA(amazonBot).getBrowser(), {name: "Amazonbot", version: "0.1", major: "0", type: "bot"}); + assert.deepEqual(botParser.setUA(claudeBot).getBrowser(), {name: "ClaudeBot", version: undefined, major: undefined, type: "bot"}); + assert.deepEqual(botParser.setUA(claudeBot2).getBrowser(), {name: "ClaudeBot", version: "1.0", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(claudeWeb).getBrowser(), {name: "Claude-Web", version: "1.0", major: "1", type: "bot"}); + assert.deepEqual(botParser.setUA(bytespider).getBrowser(), {name: "Bytespider", version: undefined, major: undefined, type: "bot"}); assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8", type: undefined}); From db3423a76c0b56400216cc27701d47c89c6b65a4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 7 Jun 2024 23:59:24 +0700 Subject: [PATCH 202/388] BREAKING - Remove `bot` type, divide as `crawler` / `fetcher` Add new crawler: Baiduspider, DuckDuckBot, & Sogou Web Spider Add new fetcher: Mastodon, Pinterestbot, Redditbot, LinkedInBot, Discordbot, Telegrambot, Twitterbot, Snapchat Bot, WhatsApp --- src/enums/ua-parser-enums.js | 3 +- src/extensions/ua-parser-extensions.d.ts | 3 +- src/extensions/ua-parser-extensions.js | 98 ++++++++++++++++++------ src/main/ua-parser.d.ts | 2 +- test/dts-test.ts | 2 +- 5 files changed, 81 insertions(+), 27 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 27afb2ede..ea1ce8706 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -147,9 +147,10 @@ const Browser = Object.freeze({ }); const BrowserType = Object.freeze({ - BOT: 'bot', + CRAWLER: 'crawler', CLI: 'cli', EMAIL: 'email', + FETCHER: 'fetcher', INAPP: 'inapp', MODULE: 'module' }); diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 4be4e3011..893f347c9 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -4,10 +4,11 @@ import type { UAParserExt } from "../main/ua-parser"; -export const Bots: UAParserExt; export const CLIs: UAParserExt; +export const Crawlers: UAParserExt; export const ExtraDevices: UAParserExt; export const Emails: UAParserExt; +export const Fetchers: UAParserExt; export const InApps: UAParserExt; export const MediaPlayers: UAParserExt; export const Modules: UAParserExt; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 8e416c13c..0903ff321 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -14,36 +14,59 @@ const VENDOR = 'vendor'; const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; -const BOT = 'bot'; +const CRAWLER = 'crawler'; const CLI = 'cli'; const EMAIL = 'email'; +const FETCHER = 'fetcher'; const INAPP = 'inapp'; const MODULE = 'module'; -const Bots = Object.freeze({ +const CLIs = Object.freeze({ browser : [ - // Googlebot / BingBot / MSNBot / FacebookBot - // GPTBot - https://platform.openai.com/docs/gptbot - // YandexBot - https://yandex.com/bots - // Applebot - http://apple.com/go/applebot - // Amazonbot - https://developer.amazon.com/amazonbot - [/((?:google|bing|msn|facebook|gpt|yandex|apple|amazon)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, BOT]], - - // Slackbot - https://api.slack.com/robots - [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, BOT]], - - // ClaudeBot / Bytespider - [/(claude(?:bot|-web)|bytespider)\/?([\w\.]*)/i], [NAME, VERSION, [TYPE, BOT]], - - // Yandex Bots - https://yandex.com/bots - [/http:\/\/(yandex).com\/(bot)s/i], [NAME, TYPE] + // wget / curl / lynx + [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); -const CLIs = Object.freeze({ +const Crawlers = Object.freeze({ browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] + // Amazonbot - https://developer.amazon.com/amazonbot + // Applebot - http://apple.com/go/applebot + // Bingbot - http://www.bing.com/bingbot.htm + // DuckDuckBot - http://duckduckgo.com/duckduckbot.html + // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ + // GPTBot - https://platform.openai.com/docs/gptbot + [/((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 + [/(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Bytespider + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + [/((?:bytespider|(?=yahoo! )slurp))/i], + [NAME, [TYPE, CRAWLER]], + + // ClaudeBot + [/(claude(?:bot|-web))\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Googlebot - http://www.google.com/bot.html + [ + /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i + ], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Sogou Spider + [/(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Yandex Bots - https://yandex.com/bots + [ + /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i + ], + [NAME, VERSION, [TYPE, CRAWLER]] ] }); @@ -125,11 +148,39 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ - // Microsoft Outlook / Thunderbird + // Microsoft Outlook / Thunderbird [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] ] }); +const Fetchers = Object.freeze({ + browser : [ + // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot + [/(bingpreview|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // Google Bots / Snapchat + [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], + [NAME, [TYPE, FETCHER]], + + + // Slackbot - https://api.slack.com/robots + [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // WhatsApp + [/(whatsapp)\/([\w\.]+)[\/ ][ianw]/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // Yandex Bots - https://yandex.com/bots + [ + /(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(yandex(?:sitelinks|userproxy))/i + ], + [NAME, VERSION, [TYPE, FETCHER]] + ] +}); + const InApps = Object.freeze({ browser : [ [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] @@ -244,16 +295,17 @@ const MediaPlayers = Object.freeze({ const Modules = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy + // Axios/jsdom/Scrapy [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]] ] }); module.exports = { - Bots, CLIs, + Crawlers, ExtraDevices, Emails, + Fetchers, InApps, MediaPlayers, Modules diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 2a44d93c6..0c3594602 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -15,7 +15,7 @@ declare namespace UAParser { name?: string; version?: string; major?: string; - type?: 'bot' | 'cli' | 'email' | 'inapp' | 'module'; + type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module'; } interface ICPU extends IData { diff --git a/test/dts-test.ts b/test/dts-test.ts index a94349007..d8a8fc215 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -28,7 +28,7 @@ expectType(browser); expectType(browser.name); expectType(browser.version); expectType(browser.major); -expectType<'bot' | 'cli' | 'email' | 'inapp' | 'module' | undefined>(browser.type); +expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module' | undefined>(browser.type); expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); From bdcd927304d73c4df7645f6c515f42d8f870989f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Jun 2024 00:07:16 +0700 Subject: [PATCH 203/388] Update test for extensions --- package.json | 2 +- src/extensions/ua-parser-extensions.js | 32 ++++++ test/mocha-test-extension.js | 146 ++++++++++++------------- test/specs/browser-clis.json | 42 +++++++ test/specs/browser-crawlers.json | 92 ++++++++++++++++ test/specs/browser-emails.json | 12 ++ test/specs/browser-fetchers.json | 12 ++ test/specs/browser-modules.json | 12 ++ 8 files changed, 272 insertions(+), 78 deletions(-) create mode 100644 test/specs/browser-clis.json create mode 100644 test/specs/browser-crawlers.json create mode 100644 test/specs/browser-emails.json create mode 100644 test/specs/browser-fetchers.json create mode 100644 test/specs/browser-modules.json diff --git a/package.json b/package.json index 5e067dd25..b4d336218 100755 --- a/package.json +++ b/package.json @@ -206,7 +206,7 @@ "test:eslint": "eslint src && eslint script", "test:jshint": "jshint src/main", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", - "test:mocha": "mocha -R list test/mocha*js", + "test:mocha": "mocha test/mocha*js", "test:playwright": "playwright test" }, "devDependencies": { diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 0903ff321..760f5aa25 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -21,6 +21,10 @@ const FETCHER = 'fetcher'; const INAPP = 'inapp'; const MODULE = 'module'; +////////////////////// +// COMMAND LINE APPS +///////////////////// + const CLIs = Object.freeze({ browser : [ // wget / curl / lynx @@ -28,6 +32,10 @@ const CLIs = Object.freeze({ ] }); +//////////////////////// +// CRAWLERS / SPIDERS +/////////////////////// + const Crawlers = Object.freeze({ browser : [ // Amazonbot - https://developer.amazon.com/amazonbot @@ -70,6 +78,10 @@ const Crawlers = Object.freeze({ ] }); +////////////////// +// EXTRA DEVICES +///////////////// + const ExtraDevices = Object.freeze({ device : [[ /(nook)[\w ]+build\/(\w+)/i, // Nook @@ -146,6 +158,10 @@ const ExtraDevices = Object.freeze({ ] }); +/////////////// +// EMAIL APPS +////////////// + const Emails = Object.freeze({ browser : [ // Microsoft Outlook / Thunderbird @@ -153,6 +169,10 @@ const Emails = Object.freeze({ ] }); +/////////////////////// +// ON-DEMAND SCRAPERS +////////////////////// + const Fetchers = Object.freeze({ browser : [ // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot @@ -181,12 +201,20 @@ const Fetchers = Object.freeze({ ] }); +//////////////////// +// IN-APP BROWSERS +/////////////////// + const InApps = Object.freeze({ browser : [ [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] ] }); +////////////////////// +// MEDIA PLAYER APPS +///////////////////// + const MediaPlayers = Object.freeze({ browser : [[ @@ -293,6 +321,10 @@ const MediaPlayers = Object.freeze({ ] }); +//////////////////////// +// MODULES / LIBRARIES +/////////////////////// + const Modules = Object.freeze({ browser : [ // Axios/jsdom/Scrapy diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index ca2251b57..c2ee99d89 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -4,98 +4,90 @@ const parseJS = require('@babel/parser').parse; const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); const { UAParser } = require('../src/main/ua-parser'); -const { Bots, CLIs, Emails, Modules } = require('../src/extensions/ua-parser-extensions'); +const clis = require('./specs/browser-clis.json'); +const crawlers = require('./specs/browser-crawlers.json'); +const emails = require('./specs/browser-emails.json'); +const fetchers = require('./specs/browser-fetchers.json'); +const modules = require('./specs/browser-modules.json'); +const { CLIs, Crawlers, Emails, Fetchers, Modules } = require('../src/extensions/ua-parser-extensions'); -describe('Bots', () => { - it('Can detect bots', () => { - const googleBot = 'Googlebot-Video/1.0'; - const gptBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)'; - const msnBot = 'msnbot-media/1.1 (+http://search.msn.com/msnbot.htm)'; - const bingPreview = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b'; - const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)'; - const wget = 'Wget/1.21.1'; - const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)'; - const yandexBot = 'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)'; - const yandexMobileScreenShotBot ='Mozilla/5.0 (compatible; YandexMobileScreenShotBot/1.0; +http://yandex.com/bots)'; - const appleBot = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)'; - const amazonBot = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML\, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)'; - const claudeBot = 'ClaudeBot'; - const claudeBot2 = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)'; - const claudeWeb = 'Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)'; - const bytespider = 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider'; - const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; - const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; - const axios = 'axios/1.3.5'; - const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; - const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; +describe('Extensions', () => { + [ + ['CLIs', clis, CLIs], + ['Crawlers', crawlers, Crawlers], + ['Emails', emails, Emails], + ['Fetchers', fetchers, Fetchers], + ['Modules', modules, Modules] + ] + .forEach((list) => { + describe(list[0], () => { + list[1].forEach((agent) => { + it(`Can detect ${agent.desc}`, () => { + let browser = UAParser(agent.ua, list[2]).browser; + assert.strictEqual(String(browser.name), agent.expect.name); + assert.strictEqual(String(browser.version), agent.expect.version); + assert.strictEqual(String(browser.type), agent.expect.type); + }); + }); + }); + }); - const botParser = new UAParser(Bots); - assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(yandexBot).getBrowser(), {name: "YandexBot", version: "3.0", major: "3", type: "bot"}); - assert.deepEqual(botParser.setUA(yandexMobileScreenShotBot).getBrowser(), {name: "yandex", version: undefined, major: undefined, type: "bot"}); - assert.deepEqual(botParser.setUA(appleBot).getBrowser(), {name: "Applebot", version: "0.1", major: "0", type: "bot"}); - assert.deepEqual(botParser.setUA(amazonBot).getBrowser(), {name: "Amazonbot", version: "0.1", major: "0", type: "bot"}); - assert.deepEqual(botParser.setUA(claudeBot).getBrowser(), {name: "ClaudeBot", version: undefined, major: undefined, type: "bot"}); - assert.deepEqual(botParser.setUA(claudeBot2).getBrowser(), {name: "ClaudeBot", version: "1.0", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(claudeWeb).getBrowser(), {name: "Claude-Web", version: "1.0", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(bytespider).getBrowser(), {name: "Bytespider", version: undefined, major: undefined, type: "bot"}); - assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"}); - assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8", type: undefined}); + const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; + const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; + const axios = 'axios/1.3.5'; + const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; + const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; - // try merging Bots & CLIs - const botsAndCLIs = { browser : [...Bots.browser, ...CLIs.browser]}; - const botsAndCLIsParser = new UAParser(botsAndCLIs); - assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); - assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); + const emailParser = new UAParser(Emails); + assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); + assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); - // alternative merge options - const botsAndCLIsParser2 = new UAParser([Bots, CLIs]); - const botsAndCLIsParser3 = new UAParser(facebookBot, [Bots, CLIs]); - assert.deepEqual(botsAndCLIsParser2.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); - assert.deepEqual(botsAndCLIsParser3.getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"}); + const moduleParser = new UAParser(Modules); + assert.deepEqual(moduleParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "module"}); + assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"}); + assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"}); +}); - const emailParser = new UAParser(Emails); - assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); - assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); +describe('Merge', () => { + it('Can merge multiple extensions', () => { + const wget = 'Wget/1.21.1'; + const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)'; - const moduleParser = new UAParser(Modules); - assert.deepEqual(moduleParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "module"}); - assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"}); - assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"}); + // try merging crawlers & CLIs + const crawlersAndCLIs = { browser : [...Crawlers.browser, ...CLIs.browser]}; + const crawlersAndCLIsParser = new UAParser(crawlersAndCLIs); + assert.deepEqual(crawlersAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); + assert.deepEqual(crawlersAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"crawler"}); + + // alternative merge options + const crawlersAndCLIsParser2 = new UAParser([Crawlers, CLIs]); + const crawlersAndCLIsParser3 = new UAParser(facebookBot, [Crawlers, CLIs]); + assert.deepEqual(crawlersAndCLIsParser2.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"}); + assert.deepEqual(crawlersAndCLIsParser3.getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"crawler"}); }); }); -// TODO : move test spec to JSON file - describe('Testing regexes', () => { let regexes; - - before('Read main js file', () => { - let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString(); - let ast = parseJS(code, { sourceType: 'script' }); - regexes = []; - traverse(ast, { - RegExpLiteral: (path) => { - regexes.push(path.node.pattern); - } - }); - if (regexes.length === 0) { - throw new Error('Regexes cannot be empty!'); + let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString(); + let ast = parseJS(code, { sourceType: 'script' }); + regexes = []; + traverse(ast, { + RegExpLiteral: (path) => { + regexes.push(path.node.pattern); } }); - describe('Begin testing', () => { - it('all regexes in extension file', () => { - regexes.forEach(regex => { - describe('Test against `safe-regex` : ' + regex, () => { - it('should be safe from potentially vulnerable regex', () => { - assert.strictEqual(safe(regex), true); - }); - }); + if (regexes.length === 0) { + throw new Error('Regexes cannot be empty!'); + } + + describe('Checking for potentially vulnerable regex', () => { + for (let regex of regexes) { + it('Test against `safe-regex` : ' + regex, () => { + assert.strictEqual(safe(regex), true); }); - }); + } }); }); \ No newline at end of file diff --git a/test/specs/browser-clis.json b/test/specs/browser-clis.json new file mode 100644 index 000000000..7d344ff15 --- /dev/null +++ b/test/specs/browser-clis.json @@ -0,0 +1,42 @@ +[ + { + "desc" : "curl", + "ua" : "curl/7.38.0", + "expect" : + { + "name" : "curl", + "version" : "7.38.0", + "type" : "cli" + } + }, + { + "desc" : "lynx", + "ua" : "Lynx 2.8.8dev.3", + "expect" : + { + "name" : "Lynx", + "version" : "2.8.8dev.3", + "type" : "cli" + } + }, + { + "desc" : "lynx", + "ua" : "Lynx/2.6", + "expect" : + { + "name" : "Lynx", + "version" : "2.6", + "type" : "cli" + } + }, + { + "desc" : "wget", + "ua" : "Wget/1.21.1", + "expect" : + { + "name" : "Wget", + "version" : "1.21.1", + "type" : "cli" + } + } +] diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json new file mode 100644 index 000000000..b72beb287 --- /dev/null +++ b/test/specs/browser-crawlers.json @@ -0,0 +1,92 @@ +[ + { + "desc" : "Applebot", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)", + "expect" : + { + "name" : "Applebot", + "version" : "0.1", + "type" : "crawler" + } + }, + { + "desc" : "Amazonbot", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)", + "expect" : + { + "name" : "Amazonbot", + "version" : "0.1", + "type" : "crawler" + } + }, + { + "desc" : "Bytespider", + "ua" : "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider", + "expect" : + { + "name" : "Bytespider", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "ClaudeBot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)", + "expect" : + { + "name" : "ClaudeBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "ClaudeWeb", + "ua" : "Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)", + "expect" : + { + "name" : "Claude-Web", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "FacebookBot", + "ua" : "Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/", + "expect" : + { + "name" : "FacebookBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot-Video", + "ua" : "Googlebot-Video/1.0", + "expect" : + { + "name" : "Googlebot-Video", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "GPTBot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)", + "expect" : + { + "name" : "GPTBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexBot", + "ua" : "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexBot", + "version" : "3.0", + "type" : "crawler" + } + } +] diff --git a/test/specs/browser-emails.json b/test/specs/browser-emails.json new file mode 100644 index 000000000..1ce6b3029 --- /dev/null +++ b/test/specs/browser-emails.json @@ -0,0 +1,12 @@ +[ + { + "desc" : "Thunderbird", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0", + "expect" : + { + "name" : "Thunderbird", + "version" : "78.13.0", + "type" : "email" + } + } +] diff --git a/test/specs/browser-fetchers.json b/test/specs/browser-fetchers.json new file mode 100644 index 000000000..646f07b2a --- /dev/null +++ b/test/specs/browser-fetchers.json @@ -0,0 +1,12 @@ +[ + { + "desc" : "BingPreview", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b", + "expect" : + { + "name" : "BingPreview", + "version" : "1.0b", + "type" : "fetcher" + } + } +] diff --git a/test/specs/browser-modules.json b/test/specs/browser-modules.json new file mode 100644 index 000000000..1639a1973 --- /dev/null +++ b/test/specs/browser-modules.json @@ -0,0 +1,12 @@ +[ + { + "desc" : "Scrapy", + "ua" : "Scrapy/1.5.0 (+https://scrapy.org)", + "expect" : + { + "name" : "Scrapy", + "version" : "1.5.0", + "type" : "module" + } + } +] From 5328642e1831e4e7adf6a9dc994e997c63e1bfff Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Jun 2024 01:17:52 +0700 Subject: [PATCH 204/388] Update version to `2.0.0-beta.3` --- CHANGELOG.md | 14 +++ README.md | 15 ++- dist/ua-parser.min.js | 6 +- dist/ua-parser.pack.js | 6 +- package-lock.json | 7 +- package.js | 2 +- package.json | 5 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 23 +++- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 142 +++++++++++++++++++---- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 2 +- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 6 +- src/main/ua-parser.mjs | 109 ++++++++++------- 18 files changed, 250 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3541fe62..cc30afa4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,20 @@ - Provided Extensions submodule `'ua-parser-js/extensions'` - Provided Helpers submodule `'ua-parser-js/helpers'` +## Version 2.0.0-beta.3 + +- Breaking: + - AR/VR devices moved to new device type: `xr` + - New property in `browser`: `type` + - In `ua-parser-js/extensions` submodule, `bots` divided into `crawler` / `fetcher` +- New features: + - Parse directly from command line using `npx ua-parser-js` + - Extensions can be passed as a list to `UAParser()` +- Add new browser: Pico Browser, Twitter, Wolvic +- Improve browser detection: DuckDuckGo, ICEBrowser, Klar, QQ, Sleipnir +- Improve device detection: Oculus Quest & Oppo Pad +- Update latest client hints spec: `formFactor` -> `formFactors` + ## Version 2.0.0-beta.2 - Increase UA_MAX_LENGTH to 500 diff --git a/README.md b/README.md index e1afb0594..116870e39 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ # UAParser.js -The most comprehensive, compact, & up-to-date JavaScript library to detect +The most comprehensive, compact, & up-to-date isomorphic JavaScript library to detect user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser (client-side) or node.js (server-side). @@ -176,11 +176,11 @@ user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser Price - FREE - FREE - $12 - $25 - $500 + FREE (License) + FREE (License) + $12 (License) + $25 (License) + $500 (License) @@ -205,8 +205,7 @@ see what's new & breaking. ## Contributors -Large or small, your contribution is valuable here. Please read [CONTRIBUTING](CONTRIBUTING.md) -guide first for the instruction details. +Please read [CONTRIBUTING](CONTRIBUTING.md) guide first for the instruction details. diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index f23c2c4e9..8819c5a6f 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta.2 - Copyright © 2012-2023 Faisal Salman +/* UAParser.js v2.0.0-beta.3 + Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTOR="formFactor",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTOR=CH_HEADER+"-form-factor",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTOR,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){mergedRegexes[i]=extensions[i]&&extensions[i].length%2===0?extensions[i].concat(regexes[i]):regexes[i]}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTOR]){var ff;if(typeof uaCH[FORMFACTOR]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 320b5712a..5b538966c 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta.2 - Copyright © 2012-2023 Faisal Salman +/* UAParser.js v2.0.0-beta.3 + Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -!function(i,u){"use strict";function e(i){for(var e={},t=0;t_?Ti(i,_):i),this}]]).setUA(o),this}Hi.VERSION="2.0.0-beta.2",Hi.BROWSER=e([f,v,p]),Hi.CPU=e([x]),Hi.DEVICE=e([h,g,m,k,y,t,r,o,a]),Hi.ENGINE=Hi.OS=e([f,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Hi),exports.UAParser=Hi):typeof define===c&&define.amd?define(function(){return Hi}):di&&(i.UAParser=Hi);var Ei,Mi=di&&(i.jQuery||i.Zepto);Mi&&!Mi.ua&&(Ei=new Hi,Mi.ua=Ei.getResult(),Mi.ua.get=function(){return Ei.getUA()},Mi.ua.set=function(i){Ei.setUA(i);var e,t=Ei.getResult();for(e in t)Mi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,c){"use strict";function e(i){for(var e={},t=0;t_?Si(i,_):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-beta.3",Ui.BROWSER=e([f,v,p,m]),Ui.CPU=e([x]),Ui.DEVICE=e([h,g,m,k,y,t,r,o,a]),Ui.ENGINE=Ui.OS=e([f,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):ui&&(i.UAParser=Ui);var Hi,Ei=ui&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(Hi=new Ui,Ei.ua=Hi.getResult(),Ei.ua.get=function(){return Hi.getUA()},Ei.ua.set=function(i){Hi.setUA(i);var e,t=Hi.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 818a3bc68..71498414b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "funding": [ { "type": "opencollective", @@ -22,6 +22,9 @@ } ], "license": "AGPL-3.0-or-later", + "bin": { + "ua-parser-js": "script/cli.js" + }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.23.2", diff --git a/package.js b/package.js index d58154c96..0ff949d95 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'faisalman:ua-parser-js', - version: '2.0.0-beta.2', + version: '2.0.0-beta.3', summary: 'Lightweight JavaScript-based user-agent string parser', git: 'https://github.com/faisalman/ua-parser-js.git', documentation: 'readme.md' diff --git a/package.json b/package.json index b4d336218..35fb6d143 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ @@ -18,7 +18,8 @@ "ua-parser-js", "browser-detection", "device-detection", - "os-detection" + "os-detection", + "bot-detection" ], "homepage": "https://github.com/faisalman/ua-parser-js", "contributors": [ diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ea1ce8706..e60c4826f 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta.2 +/* Enums for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 756194259..92aa605fd 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta.2 +/* Enums for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -54,7 +54,7 @@ const Browser = Object.freeze({ FENNEC: 'Fennec', FLOCK: 'Flock', FLOW: 'Flow', - GO: 'Go Browser', + GO: 'GoBrowser', GOOGLE_SEARCH: 'GSA', HEYTAP: 'HeyTap', HUAWEI: 'Huawei Browser', @@ -108,6 +108,7 @@ const Browser = Object.freeze({ PALEMOON: 'PaleMoon', PHANTOMJS: 'PhantomJS', PHOENIX: 'Phoenix', + PICOBROWSER: 'Pico Browser', POLARIS: 'Polaris', PUFFIN: 'Puffin', QQ: 'QQBrowser', @@ -132,9 +133,9 @@ const Browser = Object.freeze({ TESLA: 'Tesla', TIKTOK: 'TikTok', TIZEN: 'Tizen Browser', + TWITTER: 'Twitter', UC: 'UCBrowser', UP: 'UP.Browser', - VIERA: 'Viera', VIVALDI: 'Vivaldi', VIVO: 'Vivo Browser', W3M: 'w3m', @@ -143,11 +144,21 @@ const Browser = Object.freeze({ WECHAT: 'WeChat', WEIBO: 'Weibo', WHALE: 'Whale', + WOLVIC: 'Wolvic', YANDEX: 'Yandex' // TODO : test! }); +const BrowserType = Object.freeze({ + CRAWLER: 'crawler', + CLI: 'cli', + EMAIL: 'email', + FETCHER: 'fetcher', + INAPP: 'inapp', + MODULE: 'module' +}); + const CPU = Object.freeze({ ARM : 'arm', ARM_64: 'arm64', @@ -175,7 +186,8 @@ const Device = Object.freeze({ MOBILE: 'mobile', SMARTTV: 'smarttv', TABLET: 'tablet', - WEARABLE: 'wearable' + WEARABLE: 'wearable', + XR: 'xr' }); const Vendor = Object.freeze({ @@ -345,7 +357,8 @@ const OS = Object.freeze({ }); export { - Browser, + Browser, + BrowserType, CPU, Device, Vendor, diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 893f347c9..5d6f300de 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.2 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 760f5aa25..55a0a0a86 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta.2 +/* Extensions for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index e0f9c8acf..9bec89023 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta.2 +/* Extensions for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -18,32 +18,73 @@ const VENDOR = 'vendor'; const VERSION = 'version'; const MOBILE = 'mobile'; const TABLET = 'tablet'; +const CRAWLER = 'crawler'; +const CLI = 'cli'; +const EMAIL = 'email'; +const FETCHER = 'fetcher'; +const INAPP = 'inapp'; +const MODULE = 'module'; -const Apps = Object.freeze({ +////////////////////// +// COMMAND LINE APPS +///////////////////// + +const CLIs = Object.freeze({ browser : [ - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']] + // wget / curl / lynx + [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); -const Bots = Object.freeze({ - browser : [ - // Googlebot / BingBot / MSNBot / FacebookBot - [/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], +//////////////////////// +// CRAWLERS / SPIDERS +/////////////////////// +const Crawlers = Object.freeze({ + browser : [ + // Amazonbot - https://developer.amazon.com/amazonbot + // Applebot - http://apple.com/go/applebot + // Bingbot - http://www.bing.com/bingbot.htm + // DuckDuckBot - http://duckduckgo.com/duckduckbot.html + // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot - [/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']], - - // Slackbot - https://api.slack.com/robots - [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']] + [/((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 + [/(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Bytespider + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + [/((?:bytespider|(?=yahoo! )slurp))/i], + [NAME, [TYPE, CRAWLER]], + + // ClaudeBot + [/(claude(?:bot|-web))\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Googlebot - http://www.google.com/bot.html + [ + /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i + ], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Sogou Spider + [/(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, CRAWLER]], + + // Yandex Bots - https://yandex.com/bots + [ + /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i + ], + [NAME, VERSION, [TYPE, CRAWLER]] ] }); -const CLIs = Object.freeze({ - browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] - ] -}); +////////////////// +// EXTRA DEVICES +///////////////// const ExtraDevices = Object.freeze({ device : [[ @@ -121,13 +162,63 @@ const ExtraDevices = Object.freeze({ ] }); +/////////////// +// EMAIL APPS +////////////// + const Emails = Object.freeze({ browser : [ - // Microsoft Outlook / Thunderbird - [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] + // Microsoft Outlook / Thunderbird + [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] ] }); +/////////////////////// +// ON-DEMAND SCRAPERS +////////////////////// + +const Fetchers = Object.freeze({ + browser : [ + // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot + [/(bingpreview|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // Google Bots / Snapchat + [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], + [NAME, [TYPE, FETCHER]], + + + // Slackbot - https://api.slack.com/robots + [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // WhatsApp + [/(whatsapp)\/([\w\.]+)[\/ ][ianw]/i], + [NAME, VERSION, [TYPE, FETCHER]], + + // Yandex Bots - https://yandex.com/bots + [ + /(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(yandex(?:sitelinks|userproxy))/i + ], + [NAME, VERSION, [TYPE, FETCHER]] + ] +}); + +//////////////////// +// IN-APP BROWSERS +/////////////////// + +const InApps = Object.freeze({ + browser : [ + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] + ] +}); + +////////////////////// +// MEDIA PLAYER APPS +///////////////////// + const MediaPlayers = Object.freeze({ browser : [[ @@ -234,19 +325,24 @@ const MediaPlayers = Object.freeze({ ] }); +//////////////////////// +// MODULES / LIBRARIES +/////////////////////// + const Modules = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']] + // Axios/jsdom/Scrapy + [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]] ] }); export { - Apps, - Bots, CLIs, + Crawlers, ExtraDevices, Emails, + Fetchers, + InApps, MediaPlayers, Modules }; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 81023616b..89f64f0cf 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.2 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 17eea7a86..4cbf30eb8 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-beta.2 +/* Helpers for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index d710c2880..cc3533d2b 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-beta.2 +/* Helpers for UAParser.js v2.0.0-beta.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 0c3594602..2fa75c759 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0-beta.2 +// Type definitions for UAParser.js v2.0.0-beta.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index eda9ba451..a92cb8624 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta.2 - Copyright © 2012-2023 Faisal Salman +/* UAParser.js v2.0.0-beta.3 + Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-beta.2', + var LIBVERSION = '2.0.0-beta.3', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 0f4c2a87b..e0e4f4ed3 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,8 +3,8 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta.2 - Copyright © 2012-2023 Faisal Salman +/* UAParser.js v2.0.0-beta.3 + Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-beta.2', + var LIBVERSION = '2.0.0-beta.3', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -40,11 +40,12 @@ TABLET = 'tablet', SMARTTV = 'smarttv', WEARABLE = 'wearable', + XR = 'xr', EMBEDDED = 'embedded', USER_AGENT = 'user-agent', UA_MAX_LENGTH = 500, BRANDS = 'brands', - FORMFACTOR = 'formFactor', + FORMFACTORS = 'formFactors', FULLVERLIST = 'fullVersionList', PLATFORM = 'platform', PLATFORMVER = 'platformVersion', @@ -53,12 +54,12 @@ CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_ARCH = CH_HEADER + '-arch', CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, - CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor', + CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors', CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', - CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS], + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], UA_BROWSER = 'browser', UA_CPU = 'cpu', UA_DEVICE = 'device', @@ -102,12 +103,21 @@ // Helper ////////// - var extend = function (regexes, extensions) { - var mergedRegexes = {}; - for (var i in regexes) { - mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; + var extend = function (defaultRgx, extensions) { + var mergedRgx = {}; + var extraRgx = extensions; + if (!isExtensions(extensions)) { + extraRgx = {}; + for (var i in extensions) { + for (var j in extensions[i]) { + extraRgx[j] = extensions[i][j].concat(extraRgx[j] ? extraRgx[j] : []); + } + } + } + for (var k in defaultRgx) { + mergedRgx[k] = extraRgx[k] && extraRgx[k].length % 2 === 0 ? extraRgx[k].concat(defaultRgx[k]) : defaultRgx[k]; } - return mergedRegexes; + return mergedRgx; }, enumerize = function (arr) { var enums = {}; @@ -125,9 +135,9 @@ } return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; }, - isExtensions = function (obj) { + isExtensions = function (obj, deep) { for (var prop in obj) { - return /^(browser|cpu|device|engine|os)$/.test(prop); + return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false); } }, isString = function (val) { @@ -271,12 +281,13 @@ 'RT' : 'ARM' }, - formFactorMap = { + formFactorsMap = { 'embedded' : 'Automotive', 'mobile' : 'Mobile', 'tablet' : ['Tablet', 'EInk'], 'smarttv' : 'TV', - 'wearable' : ['VR', 'XR', 'Watch'], + 'wearable' : 'Watch', + 'xr' : ['VR', 'XR'], '?' : ['Desktop', 'Unknown'], '*' : undefined }; @@ -311,17 +322,20 @@ /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu ], [VERSION, [NAME, 'Baidu']], [ /(kindle)\/([\w\.]+)/i, // Kindle - /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer + /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, + // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir // Trident based /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ + /\bddg\/([\w\.]+)/i // DuckDuckGo + ], [VERSION, [NAME, 'DuckDuckGo']], [ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser ], [VERSION, [NAME, 'UCBrowser']], [ /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser @@ -354,8 +368,10 @@ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ /\bqihu|(qi?ho?o?|360)browser/i // 360 ], [[NAME, '360' + SUFFIX_BROWSER]], [ - /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i - ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser + /\b(qq)\/([\w\.]+)/i // QQ + ], [[NAME, /(.+)/, '$1Browser'], VERSION], [ + /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i + ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser /samsungbrowser\/([\w\.]+)/i // Samsung Internet ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon @@ -368,7 +384,7 @@ /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser ], [NAME, VERSION], [ - /(lbbrowser)/i, // LieBao Browser + /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq /\[(linkedin)app\]/i // LinkedIn App for iOS & Android ], [NAME], [ @@ -381,6 +397,7 @@ /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay + /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat ], [NAME, VERSION], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS @@ -420,23 +437,24 @@ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ + /(wolvic)\/([\w\.]+)/i // Wolvic + ], [NAME, VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality ], [VERSION, [NAME, FIREFOX+' Reality']], [ /ekiohf.+(flow)\/([\w\.]+)/i, // Flow /(swiftfox)/i, // Swiftfox - /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, - // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar + /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i, + // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix /(firefox)\/([\w\.]+)/i, // Other Firefox-based /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla // Other - /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, - // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i, // Links - /panasonic;(viera)/i // Panasonic Viera - ], [NAME, VERSION], [ + /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser + /(links) \(([\w\.]+)/i // Links + ], [NAME, [VERSION, /_/g, '.']], [ /(cobalt)\/([\w\.]+)/i // Cobalt ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] @@ -523,6 +541,8 @@ /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ + /\b(opd2\d{3}a?) bui/i + ], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [ // Vivo /vivo (\w+)(?: bui|\))/i, @@ -706,12 +726,17 @@ ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ - /droid.+; (glass) \d/i // Google Glass - ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ - /(quest( 2| pro)?)/i // Oculus Quest - ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ + + /////////////////// + // XR + /////////////////// + + /droid.+; (glass) \d/i // Google Glass + ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ + /(quest( \d| pro)?)/i // Oculus Quest + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ /////////////////// // EMBEDDED @@ -842,7 +867,7 @@ var defaultProps = (function () { var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; setProps.call(props.init, [ - [UA_BROWSER, [NAME, VERSION, MAJOR]], + [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]], [UA_CPU, [ARCHITECTURE]], [UA_DEVICE, [TYPE, MODEL, VENDOR]], [UA_ENGINE, [NAME, VERSION]], @@ -970,7 +995,7 @@ [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], - [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], + [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])], [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] ]); } else { @@ -1090,15 +1115,15 @@ this.set(TYPE, CONSOLE) .set(VENDOR, MICROSOFT); } - if (uaCH[FORMFACTOR]) { + if (uaCH[FORMFACTORS]) { var ff; - if (typeof uaCH[FORMFACTOR] !== 'string') { + if (typeof uaCH[FORMFACTORS] !== 'string') { var idx = 0; - while (!ff && idx < uaCH[FORMFACTOR].length) { - ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); + while (!ff && idx < uaCH[FORMFACTORS].length) { + ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap); } } else { - ff = strMapper(uaCH[FORMFACTOR], formFactorMap); + ff = strMapper(uaCH[FORMFACTORS], formFactorsMap); } this.set(TYPE, ff); } @@ -1149,7 +1174,7 @@ function UAParser (ua, extensions, headers) { if (typeof ua === OBJ_TYPE) { - if (isExtensions(ua)) { + if (isExtensions(ua, true)) { if (typeof extensions === OBJ_TYPE) { headers = extensions; // case UAParser(extensions, headers) } @@ -1159,7 +1184,7 @@ extensions = undefined; } ua = undefined; - } else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { + } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) { headers = extensions; // case UAParser(ua, headers) extensions = undefined; } @@ -1220,7 +1245,7 @@ } UAParser.VERSION = LIBVERSION; - UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); + UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]); UAParser.CPU = enumerize([ARCHITECTURE]); UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); From 6b642e28389732d15432807b752ac6bc6090a81b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Jun 2024 20:18:56 +0700 Subject: [PATCH 205/388] [Extensions submodule] Add ChatGPT-User as fetcher --- src/extensions/ua-parser-extensions.js | 3 ++- test/specs/browser-fetchers.json | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 55a0a0a86..5975ac9ed 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -175,8 +175,9 @@ const Emails = Object.freeze({ const Fetchers = Object.freeze({ browser : [ + // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot - [/(bingpreview|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i], + [/(bingpreview|chatgpt-user|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, FETCHER]], // Google Bots / Snapchat diff --git a/test/specs/browser-fetchers.json b/test/specs/browser-fetchers.json index 646f07b2a..6b537728f 100644 --- a/test/specs/browser-fetchers.json +++ b/test/specs/browser-fetchers.json @@ -8,5 +8,15 @@ "version" : "1.0b", "type" : "fetcher" } + }, + { + "desc" : "ChatGPT-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot", + "expect" : + { + "name" : "ChatGPT-User", + "version" : "1.0", + "type" : "fetcher" + } } -] +] \ No newline at end of file From b75e4493dc2903df10d12c151713bcf7e2b75325 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 10 Jun 2024 11:43:37 +0700 Subject: [PATCH 206/388] Update changelog --- CHANGELOG.md | 53 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc30afa4a..80dfe6423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,10 @@ # UAParser.js Changelog -# Version 2.0 -- What's breaking: - - Dual-licensed under AGPLv3 or PRO License - - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` - - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` -- What's new: - - Some new methods in result object: - - Support for client hints: `withClientHints()` - - Support for feature detection: `withFeatureCheck()` - - Utility for easy comparison: `is()` - - Utility to print full-name: `toString()` - - Support for ES module `import { UAParser } from 'ua-parser-js'` - - Provided Enums submodule `'ua-parser-js/enums'` - - Provided Extensions submodule `'ua-parser-js/extensions'` - - Provided Helpers submodule `'ua-parser-js/helpers'` - ## Version 2.0.0-beta.3 - Breaking: - AR/VR devices moved to new device type: `xr` - New property in `browser`: `type` - - In `ua-parser-js/extensions` submodule, `bots` divided into `crawler` / `fetcher` - New features: - Parse directly from command line using `npx ua-parser-js` - Extensions can be passed as a list to `UAParser()` @@ -29,6 +12,7 @@ - Improve browser detection: DuckDuckGo, ICEBrowser, Klar, QQ, Sleipnir - Improve device detection: Oculus Quest & Oppo Pad - Update latest client hints spec: `formFactor` -> `formFactors` +- In `ua-parser-js/extensions` submodule, `bots` divided into `crawler` / `fetcher` ## Version 2.0.0-beta.2 @@ -64,13 +48,36 @@ - Initial work on new major version +## Version 2.0 -# Version 0.7 / 1.0 +- What's breaking: + - Dual-licensed under AGPLv3 or PRO License + - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` + - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` + - AR/VR devices moved to new device type: `xr` + - New property in `browser`: `type` +- What's new: + - Some new methods in result object: + - Support for client hints: `withClientHints()` + - Support for feature detection: `withFeatureCheck()` + - Utility for easy comparison: `is()` + - Utility to print full-name: `toString()` + - Parse directly from command line using `npx ua-parser-js` + - Extensions can be passed as a list to `UAParser()` + - Support for ES module `import { UAParser } from 'ua-parser-js'` + - Provided Enums submodule `'ua-parser-js/enums'` + - Provided Extensions submodule `'ua-parser-js/extensions'` + - Provided Helpers submodule `'ua-parser-js/helpers'` -Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. +--- -## Version 0.7.37 +## Version 0.7.38 / 1.0.38 +- Fix error on getOS() when userAgentData.platform is undefined +- Add new browser: Opera GX, Twitter +- Improve browser detection: DuckDuckGo +- Improve device detection: OPPO Pad, Oculus Quest +## Version 0.7.37 / 1.0.37 - Fix misidentified WebView token as device model - Increase UA_MAX_LENGTH to 500 - Add new browser: Alipay, Klarna, Smart Lenovo Browser, Vivo Browser @@ -143,6 +150,10 @@ Version 1.0.x is basically the equivalent of version 0.7.x. See [#536](https://g - Fix trailing comma for ES3 compatibility - Some code refactor -# Version 0.8 +## Version 0.7 / 1.0 + +Version 1.0.x is basically the equivalent of version 0.7.x (mirror/duplicate). See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. + +## Version 0.8 Version 0.8 was created by accident. This version is now deprecated and no longer maintained, please update to version 0.7 / 1.0. \ No newline at end of file From b52a7ea2682ab896e1ba521f9d07666413fa7db3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Jun 2024 00:00:59 +0700 Subject: [PATCH 207/388] Add new XR device & OS: Pico - https://www.picoxr.com/global --- src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 3 +++ test/specs/browser-all.json | 10 +++++++++ test/specs/device-all.json | 27 +++++++++++++++++++++++ test/specs/os-all.json | 42 ++++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index e60c4826f..252053849 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -224,6 +224,7 @@ const Vendor = Object.freeze({ PALM: 'Palm', PANASONIC: 'Panasonic', PEBBLE: 'Pebble', + PICO: 'Pico', POLYTRON: 'Polytron', REALME: 'Realme', RIM: 'RIM', @@ -321,6 +322,7 @@ const OS = Object.freeze({ PALM: 'Palm', PC_BSD: 'PC-BSD', PCLINUXOS: 'PCLinuxOS', + PICO: 'Pico', PLAN9: 'Plan9', PLAYSTATION: 'PlayStation', QNX: 'QNX', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a92cb8624..c80286666 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -733,6 +733,8 @@ /droid.+; (glass) \d/i // Google Glass ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ + /(pico) (4|neo3(?: link|pro)?)/i // Pico + ], [VENDOR, MODEL, [TYPE, XR]], [ /(quest( \d| pro)?)/i // Oculus Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ @@ -836,6 +838,7 @@ // Console /(nintendo|playstation) (\w+)/i, // Nintendo/Playstation /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) + /(pico) .+os([\w\.]+)/i, // Pico // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index a9b22df50..880ba08e6 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -908,6 +908,16 @@ "major" : "3" } }, + { + "desc" : "PicoBrowser", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", + "expect" : + { + "name" : "Pico Browser", + "version" : "3.3.38", + "major" : "3" + } + }, { "desc" : "PicoBrowser", "ua" : "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 252782cdb..5e08f1361 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1790,6 +1790,33 @@ "type": "smarttv" } }, + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "vendor": "PICO", + "model": "4", + "type": "xr" + } + }, + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect": { + "vendor": "PICO", + "model": "4", + "type": "xr" + } + }, + { + "desc": "Pico Neo3 Link", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "vendor": "Pico", + "model": "Neo3 Link", + "type": "xr" + } + }, { "desc": "Roku", "ua": "Mozilla/5.0 (Roku) AppleWebKit/537.36 (KHTML, like Gecko) Web/1.1 Safari/537.36", diff --git a/test/specs/os-all.json b/test/specs/os-all.json index 9ad108cbb..5e9beb288 100644 --- a/test/specs/os-all.json +++ b/test/specs/os-all.json @@ -440,6 +440,39 @@ "version" : "4" } }, + { + "desc" : "PlayStation 5", + "ua" : "Mozilla/5.0 (PlayStation 5/SmartTV) AppleWebKit/605.1.15 (KHTML, like Gecko)", + "expect" : + { + "name" : "PlayStation", + "version" : "5" + } + }, + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "name" : "PICO", + "version" : "5.8.2" + } + }, + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect": { + "name" : "PICO", + "version" : "5.4.0" + } + }, + { + "desc": "Pico Neo3 Link", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "name" : "Pico", + "version" : "5.8.4.0" + } + }, { "desc" : "Xbox 360", "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox 360) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36", @@ -476,6 +509,15 @@ "version" : "Series X" } }, + { + "desc" : "Xbox Series S", + "ua" : "Mozilla/5.0 (Compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Xbox; Xbox Series S)", + "expect" : + { + "name" : "Xbox", + "version" : "Series S" + } + }, { "desc" : "Mint", "ua" : "Opera/9.80 (X11; Linux x86_64; Edition Linux Mint) Presto/2.12.388 Version/12.16", From de4978e8d92a94350ae575745b4e8ca1a7faf62b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Jun 2024 09:12:36 +0700 Subject: [PATCH 208/388] Add `mediaplayer` as a new `browser.type` --- src/enums/ua-parser-enums.js | 1 + src/enums/ua-parser-enums.mjs | 3 ++ src/extensions/ua-parser-extensions.js | 51 +++++++++++++------------- src/main/ua-parser.d.ts | 2 +- test/dts-test.ts | 2 +- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 252053849..9d93f23b0 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -152,6 +152,7 @@ const BrowserType = Object.freeze({ EMAIL: 'email', FETCHER: 'fetcher', INAPP: 'inapp', + MEDIAPLAYER: 'mediaplayer', MODULE: 'module' }); diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 92aa605fd..539cf3059 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -156,6 +156,7 @@ const BrowserType = Object.freeze({ EMAIL: 'email', FETCHER: 'fetcher', INAPP: 'inapp', + MEDIAPLAYER: 'mediaplayer', MODULE: 'module' }); @@ -228,6 +229,7 @@ const Vendor = Object.freeze({ PALM: 'Palm', PANASONIC: 'Panasonic', PEBBLE: 'Pebble', + PICO: 'Pico', POLYTRON: 'Polytron', REALME: 'Realme', RIM: 'RIM', @@ -325,6 +327,7 @@ const OS = Object.freeze({ PALM: 'Palm', PC_BSD: 'PC-BSD', PCLINUXOS: 'PCLinuxOS', + PICO: 'Pico', PLAN9: 'Plan9', PLAYSTATION: 'PlayStation', QNX: 'QNX', diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5975ac9ed..84d391f10 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -19,6 +19,7 @@ const CLI = 'cli'; const EMAIL = 'email'; const FETCHER = 'fetcher'; const INAPP = 'inapp'; +const MEDIAPLAYER = 'mediaplayer'; const MODULE = 'module'; ////////////////////// @@ -221,13 +222,13 @@ const MediaPlayers = Object.freeze({ /(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia /(coremedia) v([\w\._]+)/i - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i, // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC @@ -235,90 +236,90 @@ const MediaPlayers = Object.freeze({ /(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD /(lg player|nexplayer)\s([\d\.]+)/i, /player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(nexplayer)\s([\w\.-]+)/i // Nexplayer - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(flrp)\/([\w\.-]+)/i // Flip Player - ], [[NAME, 'Flip Player'], VERSION], [ + ], [[NAME, 'Flip Player'], VERSION, [TYPE, MEDIAPLAYER]], [ /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit - ], [NAME], [ + ], [NAME, [TYPE, MEDIAPLAYER]], [ /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i // Gstreamer - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i, // Java/urllib/requests/wget/cURL /(lavf)([\d\.]+)/i // Lavf (FFMPEG) - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(htc_one_s)\/([\d\.]+)/i, // HTC One S - ], [[NAME, /_/g, ' '], VERSION], [ + ], [[NAME, /_/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i, // MPlayer SVN - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer /(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(mplayer)/i, // MPlayer (no other info) /(yourmuze)/i, // YourMuze /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime - ], [NAME], [ + ], [NAME, [TYPE, MEDIAPLAYER]], [ /(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(nokia\d+)\/([\w\.-]+)/i // Nokia - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(winamp)3 version ([\w\.-]+)/i, // Winamp /(winamp)\s([\w\.-]+)/i, /(winamp)mpeg\/([\w\.-]+)/i - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) // inlight radio - ], [NAME], [ + ], [NAME, [TYPE, MEDIAPLAYER]], [ /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i // QuickTime/RealMedia/RadioApp/RadioClientApplication/ // SoundTap/Totem/Stagefright/Streamium - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(smp)([\d\.]+)/i // SMP - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(vlc) media player - version ([\w\.]+)/i, // VLC Videolan /(vlc)\/([\w\.-]+)/i, /(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp /(foobar2000)\/([\d\.]+)/i, // Foobar2000 /(itunes)\/([\d\.]+)/i // iTunes - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(wmplayer)\/([\w\.-]+)/i, // Windows Media Player /(windows-media-player)\/([\w\.-]+)/i - ], [[NAME, /-/g, ' '], VERSION], [ + ], [[NAME, /-/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ /windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i, // Windows Media Server - ], [VERSION, [NAME, 'Windows']], [ + ], [VERSION, [NAME, 'Windows'], [TYPE, MEDIAPLAYER]], [ /(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(rad.io)\s([\d\.]+)/i, // Rad.io /(radio.(?:de|at|fr))\s([\d\.]+)/i - ], [[NAME, 'rad.io'], VERSION] + ], [[NAME, 'rad.io'], VERSION, [TYPE, MEDIAPLAYER]] ] }); diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 2fa75c759..b83783180 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -15,7 +15,7 @@ declare namespace UAParser { name?: string; version?: string; major?: string; - type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module'; + type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'module'; } interface ICPU extends IData { diff --git a/test/dts-test.ts b/test/dts-test.ts index d8a8fc215..e5c5065d1 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -28,7 +28,7 @@ expectType(browser); expectType(browser.name); expectType(browser.version); expectType(browser.major); -expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module' | undefined>(browser.type); +expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'module' | undefined>(browser.type); expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); From 4fa991be16d093a6bf4afaffbd505f9652882690 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 16 Jun 2024 00:10:21 +0700 Subject: [PATCH 209/388] Update README.md --- README.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 116870e39..72cb670b1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,86 @@ The most comprehensive, compact, & up-to-date isomorphic JavaScript library to d user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser (client-side) or node.js (server-side). -## License Options +# Overview + +```js +import { UAParser } from 'ua-parser-js'; + +// 1. Problem: +// Imagine getting this wild user-agent string from a visitor: +const ua = `Mozilla/5.0 (Linux; Android 10; STK-LX1 +Build/HONORSTK-LX1; wv) AppleWebKit/537.36 (KHTML, +like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile +Safari/537.36 musical_ly_2022803040 JsSdk/1.0 +NetType/WIFI Channel/huaweiadsglobal_int +AppName/musical_ly app_version/28.3.4 ByteLocale/en +ByteFullLocale/en Region/IQ Spark/1.2.7-alpha.8 +AppVersion/28.3.4 PIA/1.5.11 BytedanceWebview/d8a21c6`; +// Note: this is a real user-agent (what???) + +// 2. Solution: +// Just pass the complex user-agent string to `UAParser` +const parser = new UAParser(ua); + +// 3. Result: +// And voila! +console.log(parser.getBrowser()); +// { name : "TikTok", version : "28.3.4", major : "28", type: undefined } + +console.log(parser.getCPU()); +// { architecture : undefined } + +console.log(parser.getEngine()); +// { name : "Blink", version : "110.0.5481.153" } + +console.log(parser.getDevice()); +// { type : "mobile", vendor : "Huawei", model : "STK-LX1" } + +console.log(parser.getOS()); +// { name : "Android", version : "10" } + +console.log(parser.getResult()); +/* +{ + ua: "Mozilla/5.0 (Linux; Android 10; STK-LX1 Build/HONORSTK-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile Safari/537.36 musical_ly_2022803040 JsSdk/1.0 NetType/WIFI Channel/huaweiadsglobal_int AppName/musical_ly app_version/28.3.4 ByteLocale/en ByteFullLocale/en Region/IQ Spark/1.2.7-alpha.8 AppVersion/28.3.4 PIA/1.5.11 BytedanceWebview/d8a21c6", + browser: { + name: "TikTok", + version: "28.3.4", + major: "28" + }, + cpu: {}, + device: { + type: "mobile", + model: "STK-LX1", + vendor: "Huawei" + }, + engine: { + name: "Blink", + version: "110.0.5481.153" + }, + os: { + name: "Android", + version: "10" + } +} +*/ + +// 4. Conclusion: +// The visitor is browsing from a TikTok app using an Android-powered Huawei device +// Phew! Thanks, UAParser.js! +``` + + * Live demo: https://uaparser.js.org/ + +# Documentation + + * v1.0: https://github.com/faisalman/ua-parser-js/tree/1.0.38#documentation + * v2.0: https://docs.uaparser.js.org/v2 + +Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to +see what's new & breaking. + +# License Options @@ -32,8 +111,8 @@ user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser - - + + @@ -192,15 +271,6 @@ user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser
License optionsMITAGPLMIT (v1.x)AGPL (v2.x) PRO Personal PRO Business PRO Enterprise
-## Version 2.0 -Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to -see what's new & breaking. - -# Documentation - - * v1.0: https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation - * v2.0: https://docs.uaparser.js.org/v2 - # Development ## Contributors From f24e4acae2d4639770e78ee91d966edb91ae0603 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 16 Jun 2024 14:02:30 +0700 Subject: [PATCH 210/388] Update playwright test --- package.json | 2 +- script/{build-module.js => build-esm.js} | 2 +- test/playwright-test-main.spec.mjs | 152 +++++++++++++++-------- 3 files changed, 102 insertions(+), 54 deletions(-) rename script/{build-module.js => build-esm.js} (94%) diff --git a/package.json b/package.json index 35fb6d143..704d4dfed 100755 --- a/package.json +++ b/package.json @@ -199,7 +199,7 @@ ], "bin": "./script/cli.js", "scripts": { - "build": "./script/build-dist.sh && ./script/build-module.js", + "build": "./script/build-dist.sh && ./script/build-esm.js", "build+test": "npm run build && npm run test", "fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync", "test": "./script/test-all.sh", diff --git a/script/build-module.js b/script/build-esm.js similarity index 94% rename from script/build-module.js rename to script/build-esm.js index 98fb8dbb2..cd7523878 100755 --- a/script/build-module.js +++ b/script/build-esm.js @@ -7,7 +7,7 @@ const generateMJS = (module) => { let text = fs.readFileSync(src, 'utf-8'); replacements.push( - [/const (.+?)\s*=\s*require\((.+)\)/ig, 'import $1 from $2'], + [/const (.+?)\s*=\s*require\(\'(.+)\'\)/ig, 'import $1 from \'$2.mjs\''], [/module\.exports =/ig, 'export'] ); replacements.forEach(rep => { diff --git a/test/playwright-test-main.spec.mjs b/test/playwright-test-main.spec.mjs index e830bbfeb..fe4ac7cbd 100644 --- a/test/playwright-test-main.spec.mjs +++ b/test/playwright-test-main.spec.mjs @@ -4,75 +4,123 @@ import url from 'url'; const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../')}/dist/ua-parser.html`; -test.describe('test input', () => { +test.describe('Custom navigator.userAgent tests', () => { + + test('Undefined navigator.userAgent regarded as an empty user-agent string', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgent', { + value: undefined + }); + }); + await page.goto(localHtml); + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser()); + expect(uap).toHaveProperty('ua', ''); + expect(uap).toHaveProperty('browser.name', undefined); + }); +}); + +test.describe('User-defined user-agent tests', () => { + test.beforeEach(async ({ page }) => { await page.goto(localHtml); }); - test('accept empty string', async ({ page }) => { + test('Accept empty user-agent', async ({ page }) => { // @ts-ignore const uap = await page.evaluate(async () => await UAParser('')); expect(uap).toHaveProperty('ua', ''); }); + + test('Truncate very long user-agent', async ({ page }) => { + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; (R1 1.6); SLCC1; .NET CLR 2.0.50727; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618; 66760635803; runtime 11.00294; 876906799603; 97880703; 669602703; 9778063903; 877905603; 89670803; 96690803; 8878091903; 7879040603; 999608065603; 799808803; 6666059903; 669602102803; 888809342903; 696901603; 788907703; 887806555703; 97690214703; 66760903; 968909903; 796802422703; 8868026703; 889803611803; 898706903; 977806408603; 976900799903; 9897086903; 88780803; 798802301603; 9966008603; 66760703; 97890452603; 9789064803; 96990759803; 99960107703; 8868087903; 889801155603; 78890703; 8898070603; 89970603; 89970539603; 89970488703; 8789007603; 87890903; 877904603; 9887077703; 798804903; 97890264603; 967901703; 87890703; 97690420803; 79980706603; 9867086703; 996602846703; 87690803; 6989010903; 977809603; 666601903; 876905337803; 89670603; 89970200903; 786903603; 696901911703; 788905703; 896709803; 96890703; 998601903; 88980703; 666604769703; 978806603; 7988020803; 996608803; 788903297903; 98770043603; 899708803; 66960371603; 9669088903; 69990703; 99660519903; 97780603; 888801803; 9867071703; 79780803; 9779087603; 899708603; 66960456803; 898706824603; 78890299903; 99660703; 9768079803; 977901591603; 89670605603; 787903608603; 998607934903; 799808573903; 878909603; 979808146703; 9996088603; 797803154903; 69790603; 99660565603; 7869028603; 896707703; 97980965603; 976907191703; 88680703; 888809803; 69690903; 889805523703; 899707703; 997605035603; 89970029803; 9699094903; 877906803; 899707002703; 786905857603; 69890803; 97980051903; 997603978803; 9897097903; 66960141703; 7968077603; 977804603; 88980603; 989700803; 999607887803; 78690772803; 96990560903; 98970961603; 9996032903; 9699098703; 69890655603; 978903803; 698905066803; 977806903; 9789061703; 967903747703; 976900550903; 88980934703; 8878075803; 8977028703; 97980903; 9769006603; 786900803; 98770682703; 78790903; 878906967903; 87690399603; 99860976703; 796805703; 87990603; 968906803; 967904724603; 999606603; 988705903; 989702842603; 96790603; 99760703; 88980166703; 9799038903; 98670903; 697905248603; 7968043603; 66860703; 66860127903; 9779048903; 89670123903; 78890397703; 97890603; 87890803; 8789030603; 69990603; 88880763703; 9769000603; 96990203903; 978900405903; 7869022803; 699905422903; 97890703; 87990903; 878908703; 7998093903; 898702507603; 97780637603; 966907903; 896702603; 9769004803; 7869007903; 99660158803; 7899099603; 8977055803; 99660603; 7889080903; 66660981603; 997604603; 6969089803; 899701903; 9769072703; 666603903; 99860803; 997608803; 69790903; 88680756703; 979805677903; 9986047703; 89970803; 66660603; 96690903; 8997051603; 789901209803; 8977098903; 968900326803; 87790703; 98770024803; 697901794603; 69990803; 887805925803; 968908903; 97880603; 897709148703; 877909476903; 66760197703; 977908603; 698902703; 988706504803; 977802026603; 88680964703; 8878068703; 987705107903; 978902878703; 8898069803; 9768031703; 79680803; 79980803; 669609328703; 89870238703; 99960593903; 969904218703; 78890603; 9788000703; 69690630903; 889800982903; 988709748803; 7968052803; 99960007803; 969900800803; 668604817603; 66960903; 78790734603; 8868007703; 79780034903; 8878085903; 976907603; 89670830803; 877900903; 969904889703; 7978033903; 8987043903; 99860703; 979805903; 667603803; 976805348603; 999604127603; 97790701603; 78990342903; 98770672903; 87990253903; 9877027703; 97790803; 877901895603; 8789076903; 896708595603; 997601903; 799806903; 97690603; 87790371703; 667605603; 99760303703; 97680283803; 788902750803; 787909803; 79780603; 79880866903; 9986050903; 87890543903; 979800803; 97690179703; 876901603; 699909903; 96990192603; 878904903; 877904734903; 796801446903; 977904803; 9887044803; 797805565603; 98870789703; 7869093903; 87790727703; 797801232803; 666604803; 9778071903; 9799086703; 6969000903; 89670903; 8799075903; 897708903; 88680903; 97980362603; 97980503903; 889803256703; 88980388703; 789909376803; 69690703; 6969025903; 89970309903; 96690703; 877901847803; 968901903; 96690603; 88680607603; 7889001703; 789904761803; 976807703; 976902903; 878907889703; 9897014903; 896707046603; 696909903; 666603998903; 969902703; 79680421803; 9769075603; 798800192703; 97990903; 9689024903; 668604803; 969908671903; 9996094703; 69990642703; 97890895903; 977805619903; 79980859903; 88980443803; 98970649603; 997602703; 888802169903; 699907803; 667602028803; 786903283903; 997607703; 969909803; 798809925903; 9976045603; 97790903; 9789001903; 966903603; 9789069603; 968906603; 6989091803; 896701603; 6979059803; 978803903; 997606362603; 88980803; 98970803; 88880921703; 8997065703; 899700703; 698908703; 797801027903; 7889050903; 87890603; 78690703; 99660069703; 97980309903; 976800603; 666606803; 898707703; 79880019803; 66960250803; 7978049803; 88780602603; 79680903; 88880792703; 96990903; 667608603; 87790730903; 98970903; 9699032903; 8987004803; 88880703; 89770046603; 978800803; 969908903; 9798022603; 696901903; 799803703; 989703703; 668605903; 79780903; 998601371703; 796803339703; 87890922603; 898708903; 9966061903; 66960891903; 96790903; 8779050803; 98870858803; 976909298603; 9887029903; 669608703; 979806903; 878903803; 99960703; 9789086703; 979801803; 66960008703; 979806830803; 99760212703; 786906603; 797807603; 789907297703; 96990703; 786901603; 796807766603; 896702651603; 789902585603; 66660925903; 9986085703; 66960302703; 69890703; 789900703; 89970903; 9679060703; 9789002903; 979908821603; 986708140803; 976809828703; 7988082803; 79680997903; 99960803; 9788081903; 979805703; 787908603; 66960602803; 9887098703; 978803237703; 888806804603; 999604703; 977904703; 966904635703; 97680291703; 977809345603; 8878046703; 988709803; 976900773603; 989703903; 88780198603; 87790603; 986708703; 78890604703; 87790544803; 976809850903; 887806703; 987707527603; 79880803; 9897059603; 897709820603; 97880804803; 66960026703; 9789062803; 9867090803; 669600603; 8967087703; 78890903; 89770903; 97980703; 976802687603; 66860400803; 979901288603; 96990160903; 99860228903; 966900703; 66760603; 9689035703; 9779064703; 7968023603; 87890791903; 98770870603; 9798005803; 6969087903; 9779097903; 6979065703; 699903252603; 79780989703; 87690901803; 978905763903; 977809703; 97790369703; 899703269603; 8878012703; 78790803; 87690395603; 8888042803; 667607689903; 8977041803; 6666085603; 6999080703; 69990797803; 88680721603; 99660519803; 889807603; 87890146703; 699906325903; 89770603; 669608615903; 9779028803; 88880603; 97790703; 79780703; 97680355603; 6696024803; 78790784703; 97880329903; 9699077703; 89870803; 79680227903; 976905852703; 8997098903; 896704796703; 66860598803; 9897036703; 66960703; 9699094703; 9699008703; 97780485903; 999603179903; 89770834803; 96790445603; 79680460903; 9867009603; 89870328703; 799801035803; 989702903; 66960758903; 66860150803; 6686088603; 9877092803; 96990603; 99860603; 987703663603; 98870903; 699903325603; 87790803; 97680703; 8868030703; 9799030803; 89870703; 97680803; 9669054803; 6979097603; 987708046603; 999608603; 878904803; 998607408903; 968903903; 696900703; 977907491703; 6686033803; 669601803; 99960290603; 887809169903; 979803703; 69890903; 699901447903; 8987064903; 799800603; 98770903; 8997068703; 967903603; 66760146803; 978805087903; 697908138603; 799801603; 88780964903; 989708339903; 8967048603; 88880981603; 789909703; 796806603; 977905977603; 989700603; 97780703; 9669062603; 88980714603; 897709545903; 988701916703; 667604694903; 786905664603; 877900803; 886805490903; 89970559903; 99960531803; 7998033903; 98770803; 78890418703; 669600872803; 996605216603; 78690962703; 667604903; 996600903; 999608903; 9699083803; 787901803; 97780707603; 787905312703; 977805803; 8977033703; 97890708703; 989705521903; 978800703; 698905703; 78890376903; 878907703; 999602903; 986705903; 668602719603; 979901803; 997606903; 66760393903; 987703603; 78790338903; 96890803; 97680596803; 666601603; 977902178803; 877902803; 78790038603; 8868075703; 99960060603)')); + + expect(uap).toHaveProperty('ua', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; (R1 1.6); SLCC1; .NET CLR 2.0.50727; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618; 66760635803; runtime 11.00294; 876906799603; 97880703; 669602703; 9778063903; 877905603; 89670803; 96690803; 8878091903; 7879040603; 999608065603; 799808803; 6666059903; 669602102803; 888809342903; 696901603; 788907703; 887806555703; 97690214703; 66760903; 968909903; 796802422703; 8868026703; 8898036'); + }); }); -test('read client hints data', async ({ page }) => { - await page.addInitScript(() => { - Object.defineProperty(navigator, 'userAgentData', { - value: { - brands: [], - platform: '', - mobile: false, - getHighEntropyValues: () => { - return Promise.resolve({ - brands: [ - { - brand: 'Chromium', - version: '110' - }, - { - brand: 'Not(A:Brand', - version: '110' - }, - { - brand: 'New Browser', - version: '110' - } - ], - platform: 'New OS', - formFactors: 'New Form Factor' - }); +test.describe('withClientHints() tests', () => { + + test('Detect custom Client Hints data', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgentData', { + value: { + brands: [], + platform: '', + mobile: false, + getHighEntropyValues: () => { + return Promise.resolve({ + brands: [ + { + brand: 'Chromium', + version: '110' + }, + { + brand: 'Not(A:Brand', + version: '110' + }, + { + brand: 'New Browser', + version: '110' + } + ], + platform: 'New OS', + formFactors: 'New Form Factor' + }); + } } - } + }); }); + await page.goto(localHtml); + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser().withClientHints()); + expect(uap).toHaveProperty('browser.name', 'New Browser'); + expect(uap).toHaveProperty('os.name', 'New OS'); + expect(uap).toHaveProperty('device.type', undefined); }); - - await page.goto(localHtml); - - // @ts-ignore - const uap = await page.evaluate(async () => await UAParser().withClientHints()); - - expect(uap).toHaveProperty('browser.name', 'New Browser'); - expect(uap).toHaveProperty('os.name', 'New OS'); - expect(uap).toHaveProperty('device.type', undefined); }); -test('detect Brave', async ({ page }) => { - await page.addInitScript(() => { - Object.defineProperty(navigator, 'brave', { - value: { - isBrave: () => true - } +test.describe('withFeatureCheck() tests', () => { + + test('Detect Brave', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'brave', { + value: { + isBrave: () => true + } + }); }); + await page.goto(localHtml); + // @ts-ignore + let uap = await page.evaluate(() => UAParser()); + expect(uap).toHaveProperty('browser.name', 'Chrome Headless'); + // @ts-ignore + uap = await page.evaluate(() => UAParser().withFeatureCheck()); + expect(uap).toHaveProperty('browser.name', 'Brave'); }); - await page.goto(localHtml); - - // @ts-ignore - let uap = await page.evaluate(() => UAParser()); - expect(uap).toHaveProperty('browser.name', 'Chrome Headless'); - // @ts-ignore - uap = await page.evaluate(() => UAParser().withFeatureCheck()); - expect(uap).toHaveProperty('browser.name', 'Brave'); + test('Detect iPad', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgent', { + value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15' + }); + Object.defineProperty(navigator, 'standalone', { + value: true + }); + Object.defineProperty(navigator, 'maxTouchPoints', { + value: 3 + }); + }); + await page.goto(localHtml); + // @ts-ignore + let uap = await page.evaluate(() => UAParser()); + expect(uap).toHaveProperty('device.model', 'Macintosh'); + expect(uap).toHaveProperty('device.type', undefined); + // @ts-ignore + uap = await page.evaluate(() => UAParser().withFeatureCheck()); + expect(uap).toHaveProperty('device.model', 'iPad'); + expect(uap).toHaveProperty('device.type', 'tablet'); + }); }); \ No newline at end of file From 4e64cb644fb578a9cd210240fece0d95b9488cfb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 16 Jun 2024 23:11:59 +0700 Subject: [PATCH 211/388] Migrate to uaparser.dev --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- README.md | 6 +++--- package.json | 4 ++-- src/main/ua-parser.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2ba42577c..8fadfd144 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,7 +10,7 @@ assignees: '' **Library version** Which version of the library that you use, eg: v0.7.35 or v2.0.0-alpha.3 -For the issue related with detection result, you can use the demo section in https://uaparser.js.org to confirm +For the issue related with detection result, you can use the demo section in https://uaparser.dev to confirm **Describe the bug** A clear and concise description of what the bug is. diff --git a/README.md b/README.md index 72cb670b1..57e0473bf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

@@ -88,12 +88,12 @@ console.log(parser.getResult()); // Phew! Thanks, UAParser.js! ``` - * Live demo: https://uaparser.js.org/ + * Live demo: https://uaparser.dev # Documentation * v1.0: https://github.com/faisalman/ua-parser-js/tree/1.0.38#documentation - * v2.0: https://docs.uaparser.js.org/v2 + * v2.0: https://docs.uaparser.dev Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to see what's new & breaking. diff --git a/package.json b/package.json index 704d4dfed..618a794d8 100755 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "os-detection", "bot-detection" ], - "homepage": "https://github.com/faisalman/ua-parser-js", + "homepage": "https://uaparser.dev", "contributors": [ "Aamir Poonawalla ", "Admas ", @@ -237,7 +237,7 @@ "test": "test" }, "bugs": "https://github.com/faisalman/ua-parser-js/issues", - "demo": "https://uaparser.js.org", + "demo": "https://uaparser.dev", "download": "https://raw.github.com/faisalman/ua-parser-js/master/dist/ua-parser.pack.js", "funding": [ { diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index c80286666..c7034f6e5 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -4,7 +4,7 @@ AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. - Demo : https://faisalman.github.io/ua-parser-js + Demo : https://uaparser.dev Source : https://github.com/faisalman/ua-parser-js */ ///////////////////////////////////////////////////////////////////////////////// From 81cbbe504e895ef5bcb6ffda96a7fa7ecff5b730 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 16 Jun 2024 23:30:27 +0700 Subject: [PATCH 212/388] Remove package.js & move pull_request_template inside /.github --- .../PULL_REQUEST_TEMPLATE.md | 0 package.js | 12 ------------ 2 files changed, 12 deletions(-) rename PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md (100%) delete mode 100644 package.js diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/package.js b/package.js deleted file mode 100644 index 0ff949d95..000000000 --- a/package.js +++ /dev/null @@ -1,12 +0,0 @@ -Package.describe({ - name: 'faisalman:ua-parser-js', - version: '2.0.0-beta.3', - summary: 'Lightweight JavaScript-based user-agent string parser', - git: 'https://github.com/faisalman/ua-parser-js.git', - documentation: 'readme.md' -}); - -Package.onUse(function (api) { - api.addFiles("src/ua-parser.js"); - api.export("UAParser"); -}); From a74ace8dd1c8a50b5c6163bba7a9e8973dd27d0a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 22 Jun 2024 00:13:13 +0700 Subject: [PATCH 213/388] Add Yahoo! Japan to Crawler, Fetcher, & InApp browser --- src/extensions/ua-parser-extensions.js | 100 ++++++++++++------------- src/helpers/ua-parser-helpers.mjs | 2 +- test/specs/browser-crawlers.json | 10 +++ 3 files changed, 61 insertions(+), 51 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 84d391f10..bd0ee4056 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -39,43 +39,40 @@ const CLIs = Object.freeze({ const Crawlers = Object.freeze({ browser : [ - // Amazonbot - https://developer.amazon.com/amazonbot - // Applebot - http://apple.com/go/applebot - // Bingbot - http://www.bing.com/bingbot.htm - // DuckDuckBot - http://duckduckgo.com/duckduckbot.html - // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ - // GPTBot - https://platform.openai.com/docs/gptbot - [/((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i], - [NAME, VERSION, [TYPE, CRAWLER]], + [ + // Amazonbot - https://developer.amazon.com/amazonbot + // Applebot - http://apple.com/go/applebot + // Bingbot - http://www.bing.com/bingbot.htm + // DuckDuckBot - http://duckduckgo.com/duckduckbot.html + // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ + // GPTBot - https://platform.openai.com/docs/gptbot + /((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i, - // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 - [/(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i], - [NAME, VERSION, [TYPE, CRAWLER]], + // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 + /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, - // Bytespider - // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - [/((?:bytespider|(?=yahoo! )slurp))/i], - [NAME, [TYPE, CRAWLER]], + // ClaudeBot + /(claude(?:bot|-web))\/([\w\.]+)/i, - // ClaudeBot - [/(claude(?:bot|-web))\/([\w\.]+)/i], - [NAME, VERSION, [TYPE, CRAWLER]], + // Googlebot - http://www.google.com/bot.html + /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i, + + // Sogou Spider + /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, + + // Yahoo! Japan - https://support.yahoo-net.jp/PccSearch/s/article/H000007955 + /(y!?j-(?:asr|br[uw]|dscv|mmp|vsidx|wsc))\/([\w\.]+)/i, + + // Yandex Bots - https://yandex.com/bots + /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i + ], - // Googlebot - http://www.google.com/bot.html - [ - /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i - ], - [NAME, VERSION, [TYPE, CRAWLER]], - - // Sogou Spider - [/(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, CRAWLER]], - // Yandex Bots - https://yandex.com/bots - [ - /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i - ], - [NAME, VERSION, [TYPE, CRAWLER]] + // Bytespider + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + [/((?:bytespider|(?=yahoo! )slurp))/i], + [NAME, [TYPE, CRAWLER]] ] }); @@ -176,30 +173,29 @@ const Emails = Object.freeze({ const Fetchers = Object.freeze({ browser : [ - // ChatGPT-User - https://platform.openai.com/docs/plugins/bot - // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot - [/(bingpreview|chatgpt-user|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i], - [NAME, VERSION, [TYPE, FETCHER]], - - // Google Bots / Snapchat - [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], - [NAME, [TYPE, FETCHER]], + [ + // ChatGPT-User - https://platform.openai.com/docs/plugins/bot + // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot + /(bingpreview|chatgpt-user|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i, + // Slackbot - https://api.slack.com/robots + /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, + + // WhatsApp + /(whatsapp)\/([\w\.]+)[\/ ][ianw]/i, - // Slackbot - https://api.slack.com/robots - [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], - [NAME, VERSION, [TYPE, FETCHER]], + // Yahoo! Japan + /(y!?j-dlc)\/([\w\.]+)/i, - // WhatsApp - [/(whatsapp)\/([\w\.]+)[\/ ][ianw]/i], - [NAME, VERSION, [TYPE, FETCHER]], - - // Yandex Bots - https://yandex.com/bots - [ + // Yandex Bots - https://yandex.com/bots /(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, /(yandex(?:sitelinks|userproxy))/i ], - [NAME, VERSION, [TYPE, FETCHER]] + [NAME, VERSION, [TYPE, FETCHER]], + + // Google Bots / Snapchat + [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], + [NAME, [TYPE, FETCHER]], ] }); @@ -209,7 +205,11 @@ const Fetchers = Object.freeze({ const InApps = Object.freeze({ browser : [ - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] + // Slack + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]], + + // Yahoo! Japan + [/jp\.co\.yahoo\.android\.yjtop\/([\d\.]+)/i], [VERSION, 'Yahoo! Japan', [TYPE, INAPP]] ] }); diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index cc3533d2b..95d6c0f17 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -11,7 +11,7 @@ /*jshint esversion: 6 */ -import { CPU, OS, Engine } from '../enums/ua-parser-enums'; +import { CPU, OS, Engine } from '../enums/ua-parser-enums.mjs'; const isAppleSilicon = (res) => res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json index b72beb287..107ab24b4 100644 --- a/test/specs/browser-crawlers.json +++ b/test/specs/browser-crawlers.json @@ -79,6 +79,16 @@ "type" : "crawler" } }, + { + "desc" : "Yahoo! Japan", + "ua" : "Y!J-BRW/1.0 (https://www.yahoo-help.jp/app/answers/detail/p/595/a_id/42716)", + "expect" : + { + "name" : "Y!J-BRW", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "YandexBot", "ua" : "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)", From 50c15ad5a8f713a3a7215ee3bcc3df75b32642ae Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 22 Jun 2024 21:15:26 +0700 Subject: [PATCH 214/388] Add new helper method `isPWA()` to check for standalone mode. Also fix #728 --- src/helpers/ua-parser-helpers.d.ts | 4 +++- src/helpers/ua-parser-helpers.js | 13 ++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 89f64f0cf..f467c310e 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -7,9 +7,11 @@ import { IResult } from "../main/ua-parser"; declare function isAppleSilicon(res: IResult): boolean; declare function isChromiumBased(res: IResult): boolean; declare function isFrozenUA(ua: string): boolean; +declare function isPWA(): boolean; export { isAppleSilicon, isChromiumBased, - isFrozenUA + isFrozenUA, + isPWA } \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 4cbf30eb8..a862ec539 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -15,8 +15,19 @@ const isChromiumBased = (res) => res.engine.is(Engine.BLINK); const isFrozenUA = (ua) => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); +const isPWA = () => window && (window.matchMedia('(display-mode: standalone)').matches || + // iOS + navigator.standalone || + // Android + document.referrer.startsWith('android-app://') || + // Windows + window.Windows || + /trident.+(msapphost|webview)\//i.test(navigator.userAgent) || + document.referrer.startsWith('app-info://platform/microsoft-store')); + module.exports = { isAppleSilicon, isChromiumBased, - isFrozenUA + isFrozenUA, + isPWA } \ No newline at end of file From 21162f127745bef088d4ecb7fd93e27bc43a6dfb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 22 Jun 2024 22:26:58 +0700 Subject: [PATCH 215/388] BREAKING CHANGE - Rename `isChromiumBased` -> `isChromeFamily` & `isPWA` -> `isStandalonePWA` --- src/helpers/ua-parser-helpers.d.ts | 8 ++++---- src/helpers/ua-parser-helpers.js | 8 ++++---- test/dts-test.ts | 4 ++-- test/mocha-test-helpers.js | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index f467c310e..905e5edad 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -5,13 +5,13 @@ import { IResult } from "../main/ua-parser"; declare function isAppleSilicon(res: IResult): boolean; -declare function isChromiumBased(res: IResult): boolean; +declare function isChromeFamily(res: IResult): boolean; declare function isFrozenUA(ua: string): boolean; -declare function isPWA(): boolean; +declare function isStandalonePWA(): boolean; export { isAppleSilicon, - isChromiumBased, + isChromeFamily, isFrozenUA, - isPWA + isStandalonePWA } \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index a862ec539..74923e168 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -11,11 +11,11 @@ const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); const isAppleSilicon = (res) => res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); -const isChromiumBased = (res) => res.engine.is(Engine.BLINK); +const isChromeFamily = (res) => res.engine.is(Engine.BLINK); const isFrozenUA = (ua) => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); -const isPWA = () => window && (window.matchMedia('(display-mode: standalone)').matches || +const isStandalonePWA = () => window && (window.matchMedia('(display-mode: standalone)').matches || // iOS navigator.standalone || // Android @@ -27,7 +27,7 @@ const isPWA = () => window && (window.matchMedia('(display-mode: standalone)').m module.exports = { isAppleSilicon, - isChromiumBased, + isChromeFamily, isFrozenUA, - isPWA + isStandalonePWA } \ No newline at end of file diff --git a/test/dts-test.ts b/test/dts-test.ts index e5c5065d1..edf1d47d6 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -1,6 +1,6 @@ import { expectType } from 'tsd'; import { UAParser, IResult, IBrowser, ICPU, IEngine, IDevice, IOS } from "../src/main/ua-parser"; -import { isAppleSilicon, isChromiumBased } from "../src/helpers/ua-parser-helpers"; +import { isAppleSilicon, isChromeFamily } from "../src/helpers/ua-parser-helpers"; const uastring = 'Mozilla/5.0 (X11; MyCustomOS; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0'; const extensions = { @@ -46,4 +46,4 @@ expectType(parser.setUA(uastring)); const result = parser.getResult(); expectType(isAppleSilicon(result)); -expectType(isChromiumBased(result)); \ No newline at end of file +expectType(isChromeFamily(result)); \ No newline at end of file diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index faacf617c..1cfffea72 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,6 +1,6 @@ const assert = require('assert'); const { UAParser } = require('../src/main/ua-parser'); -const { isAppleSilicon, isChromiumBased } = require('../src/helpers/ua-parser-helpers'); +const { isAppleSilicon, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); describe('isAppleSilicon', () => { it('Can detect Apple Silicon device', () => { @@ -14,13 +14,13 @@ describe('isAppleSilicon', () => { }); }); -describe('isChromiumBased', () => { +describe('isChromeFamily', () => { it('Can detect Chromium-based browser', () => { const edge = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.2151.58'; const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; - assert.equal(isChromiumBased(UAParser(edge)), true); - assert.equal(isChromiumBased(UAParser(firefox)), false); + assert.equal(isChromeFamily(UAParser(edge)), true); + assert.equal(isChromeFamily(UAParser(firefox)), false); }); }); \ No newline at end of file From 654285cf9c75367a3d5945959f09d8893e363671 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 23 Jun 2024 11:59:54 +0700 Subject: [PATCH 216/388] Add new browser: Helio --- src/enums/ua-parser-enums.js | 2 +- src/main/ua-parser.js | 4 ++-- test/specs/browser-all.json | 10 ++++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 9d93f23b0..8fbee9b00 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -52,6 +52,7 @@ const Browser = Object.freeze({ FLOW: 'Flow', GO: 'GoBrowser', GOOGLE_SEARCH: 'GSA', + HELIO: 'Helio', HEYTAP: 'HeyTap', HUAWEI: 'Huawei Browser', ICAB: 'iCab', @@ -343,7 +344,6 @@ const OS = Object.freeze({ UBUNTU: 'Ubuntu', UNIX: 'Unix', VECTORLINUX: 'VectorLinux', - VIERA: 'Viera', WATCHOS: 'watchOS', WEBOS: 'WebOS', WINDOWS: 'Windows', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index c7034f6e5..1ca736304 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -327,8 +327,8 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 880ba08e6..71ae6f0a4 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -698,6 +698,16 @@ "major" : "1" } }, + { + "desc" : "Helio", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36 Helio/0.98.20", + "expect" : + { + "name" : "Helio", + "version" : "0.98.20", + "major" : "0" + } + }, { "desc" : "HeyTap", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.61 Safari/537.36 HeyTapBrowser/40.8.10.1", From c391d8a73c1a030c24845bc191b021bfb6cba256 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 27 Jul 2024 10:57:11 +0700 Subject: [PATCH 217/388] Update `isAppleSilicon()` helper method to also check for WebGL renderer info #732 --- src/helpers/ua-parser-helpers.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 74923e168..3c7d399d5 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -9,7 +9,25 @@ const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); -const isAppleSilicon = (res) => res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); +const isAppleSilicon = (res) => { + if (res.os.is(OS.MACOS)) { + if (res.cpu.is(CPU.ARM)) { + return true; + } + try { + const canvas = document.createElement('canvas'); + const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + const debug = webgl.getExtension('WEBGL_debug_renderer_info'); + const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL); + if (renderer.match(/apple m\d/i)) { + return true; + } + } catch { + return false; + } + } + return false; +} const isChromeFamily = (res) => res.engine.is(Engine.BLINK); From 62fb6c29257eec5f5521ae63882f0ca2002695bb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 27 Jul 2024 22:47:27 +0700 Subject: [PATCH 218/388] Fix #660 - Add new helper method: `getDeviceVendor()` to guess the device vendor from a model name --- src/helpers/ua-parser-helpers.d.ts | 2 ++ src/helpers/ua-parser-helpers.js | 4 ++++ test/mocha-test-helpers.js | 19 ++++++++++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 905e5edad..c58fc1463 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -4,12 +4,14 @@ import { IResult } from "../main/ua-parser"; +declare function getDeviceVendor(model: string): string | undefined; declare function isAppleSilicon(res: IResult): boolean; declare function isChromeFamily(res: IResult): boolean; declare function isFrozenUA(ua: string): boolean; declare function isStandalonePWA(): boolean; export { + getDeviceVendor, isAppleSilicon, isChromeFamily, isFrozenUA, diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 3c7d399d5..e0729c94d 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -8,6 +8,9 @@ /*jshint esversion: 6 */ const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); +const { UAParser } = require('../main/ua-parser'); + +const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; const isAppleSilicon = (res) => { if (res.os.is(OS.MACOS)) { @@ -44,6 +47,7 @@ const isStandalonePWA = () => window && (window.matchMedia('(display-mode: stand document.referrer.startsWith('app-info://platform/microsoft-store')); module.exports = { + getDeviceVendor, isAppleSilicon, isChromeFamily, isFrozenUA, diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index 1cfffea72..da9ae2c85 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,6 +1,23 @@ const assert = require('assert'); const { UAParser } = require('../src/main/ua-parser'); -const { isAppleSilicon, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); +const { getDeviceVendor, isAppleSilicon, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); + +describe('getDeviceVendor', () => { + it('Can guess the device vendor from a model name', () => { + + const modelSM = 'SM-A605G'; + const modelRedmi = 'Redmi Note 8'; + const modelNexus = 'Nexus 6P'; + const modelQuest = 'Quest 3'; + const modelAquos = 'AQUOS-TVX19B'; + + assert.equal(getDeviceVendor(modelSM), 'Samsung'); + assert.equal(getDeviceVendor(modelRedmi), 'Xiaomi'); + assert.equal(getDeviceVendor(modelNexus), 'Huawei'); + assert.equal(getDeviceVendor(modelQuest), 'Facebook'); + assert.equal(getDeviceVendor(modelAquos), 'Sharp'); + }); +}); describe('isAppleSilicon', () => { it('Can detect Apple Silicon device', () => { From 15391d2b8d5c9d2fa6b7e89d300d7521913ceff7 Mon Sep 17 00:00:00 2001 From: lj0812 Date: Wed, 31 Jul 2024 11:42:33 +0800 Subject: [PATCH 219/388] Improve browser detection for Quark (#737) Co-authored-by: lijian01 Co-authored-by: Faisal Salman --- src/main/ua-parser.js | 6 ++++-- test/specs/browser-all.json | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 1ca736304..00f3a683d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -327,11 +327,13 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ + /quark(?:pc)?\/([-\w\.]+)/i // Quark + ], [VERSION, [NAME, 'Quark']], [ /\bddg\/([\w\.]+)/i // DuckDuckGo ], [VERSION, [NAME, 'DuckDuckGo']], [ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 71ae6f0a4..d8638b06f 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1418,6 +1418,16 @@ "major" : "5" } }, + { + "desc" : "Quark", + "ua" : "mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/112.0.0.0 safari/537.36 quarkpc/1.5.5.75", + "expect" : + { + "name" : "Quark", + "version" : "1.5.5.75", + "major" : "1" + } + }, { "desc" : "QupZilla", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.8.9 Safari/538.1", From 90659494518a5b5195daac5ef7b9842d61acbe0b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 31 Jul 2024 10:16:01 +0700 Subject: [PATCH 220/388] Add new vendor: Nothing https://nothing.tech --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 4 ++++ test/specs/device-all.json | 27 +++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 8fbee9b00..c755c71ce 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -219,6 +219,7 @@ const Vendor = Object.freeze({ NEXIAN: 'Nexian', NINTENDO: 'Nintendo', NOKIA: 'Nokia', + NOTHING: 'Nothing', NVIDIA: 'Nvidia', ONEPLUS: 'OnePlus', OPPO: 'OPPO', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 00f3a683d..9b9a3c8c5 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -641,6 +641,10 @@ /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [ + // Nothing + /droid.+; (a(?:015|06[35]|142p?))/i + ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ + // MIXED /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 5e08f1361..d4dc62c4c 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1367,6 +1367,33 @@ "type": "mobile" } }, + { + "desc": "Nothing 1", + "ua": "Mozilla/5.0 (Linux; Android 13; A063) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A063", + "type": "mobile" + } + }, + { + "desc": "Nothing 2", + "ua": "Mozilla/5.0 (Linux; Android 14; A065 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.53 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A065", + "type": "mobile" + } + }, + { + "desc": "Nothing 2a", + "ua": "Mozilla/5.0 (Linux; Android 14; A142 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.71 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A142", + "type": "mobile" + } + }, { "desc": "Oculus Quest", "ua": "Mozilla/5.0 (Linux; Android 10; Quest) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/15.0.0.0.22.280317669 SamsungBrowser/4.0 Chrome/89.0.4389.90 VR Safari/537.36", From cf775f18f00959332e64c06ed1f7a56313451bf8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 31 Jul 2024 10:45:20 +0700 Subject: [PATCH 221/388] Restore unintentionally removed code from previous commit --- src/main/ua-parser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 9b9a3c8c5..cabfb839c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -327,8 +327,8 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ From ac3a3c33dcd5d487021e2ebbfbb3d5536214f542 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 4 Aug 2024 10:29:17 +0700 Subject: [PATCH 222/388] Add new device vendor: TCL https://www.tcl.com/global/en/mobile --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 4 ++ test/specs/device-all.json | 108 +++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index c755c71ce..3801a86e1 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -237,6 +237,7 @@ const Vendor = Object.freeze({ SIEMENS: 'Siemens', SONY: 'Sony', SPRINT: 'Sprint', + TCL: 'TCL', TECHNISAT: 'TechniSAT', TECNO: 'Tecno', TESLA: 'Tesla', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index cabfb839c..0b87936ed 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -628,6 +628,10 @@ /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ + // TCL + /droid [\w\.]+; ((?:8[14]9[16]|9(?:0(?:48|60|8[01])|1(?:3[27]|66)|2(?:6[69]|9[56])|466))[gqswx])\w*(\)| bui)/i + ], [MODEL, [VENDOR, 'TCL'], [TYPE, TABLET]], [ + // Acer /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [ diff --git a/test/specs/device-all.json b/test/specs/device-all.json index d4dc62c4c..0f11ef3d7 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2420,6 +2420,114 @@ "type": "smarttv" } }, + { + "desc": "TCL 10 TabMax", + "ua": "Mozilla/5.0 (Linux; Android 11; 9296Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9296Q", + "type": "tablet" + } + }, + { + "desc": "TCL 10 TabMax 4G", + "ua": "Mozilla/5.0 (Linux; Android 10; 9295G_EEA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9295G", + "type": "tablet" + } + }, + { + "desc": "TCL 10 TabMax WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; 9296G_TR) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9296G", + "type": "tablet" + } + }, + { + "desc": "TCL NxtPaper 11", + "ua": "Mozilla/5.0 (Linux; Android 13; 9466X Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.179 Safari/537.36 [FB_IAB/FB4A;FBAV/473.0.0.41.81;]", + "expect": { + "vendor": "TCL", + "model": "9466X", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 8 4G", + "ua": "Mozilla/5.0 (Linux; Android 10; 9048S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9048S", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 8 LE", + "ua": "Mozilla/5.0 (Linux; Android 12; 9137W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.61 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9137W", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 FHD 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9060G Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9060G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 HD 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9060X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9060X", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 LTE", + "ua": "Mozilla/5.0 (Linux; Android 13; 8196G Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.162 Safari/537.36 [FB_IAB/FB4A;FBAV/471.0.0.35.80;]", + "expect": { + "vendor": "TCL", + "model": "8196G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 13; 8496G Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.61 Safari/537.36 [FB_IAB/FB4A;FBAV/474.0.0.52.74;]", + "expect": { + "vendor": "TCL", + "model": "8496G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10L", + "ua": "Mozilla/5.0 (Linux; Android 11; 8491X_EEA Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.105 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "8491X", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10s 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9080G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9080G", + "type": "tablet" + } + }, { "desc": "Tecno KC8", "ua": "Mozilla/5.0 (Linux; Android 10; TECNO KC8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", From cce9060b4f3c26ce1f11c6a3a444e1a4afb5f1b1 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 6 Aug 2024 21:23:54 +0700 Subject: [PATCH 223/388] Add more TCL devices --- src/main/ua-parser.js | 5 +- test/specs/device-all.json | 369 +++++++++++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0b87936ed..b817bdf01 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -629,8 +629,11 @@ ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ // TCL - /droid [\w\.]+; ((?:8[14]9[16]|9(?:0(?:48|60|8[01])|1(?:3[27]|66)|2(?:6[69]|9[56])|466))[gqswx])\w*(\)| bui)/i + /tcl (xess p17aa)/i, + /droid [\w\.]+; ((?:8[14]9[16]|9(?:0(?:48|60|8[01])|1(?:3[27]|66)|2(?:6[69]|9[56])|466))[gqswx])(_\w(\w|\w\w))?(\)| bui)/i ], [MODEL, [VENDOR, 'TCL'], [TYPE, TABLET]], [ + /droid [\w\.]+; (418(?:7d|8v)|5087z|5102l|61(?:02[dh]|25[adfh]|27[ai]|56[dh]|59k|65[ah])|a509dl|t(?:43(?:0w|1[adepqu])|50(?:6d|7[adju])|6(?:09dl|10k|12b|71[efho]|76[hjk])|7(?:66[ahju]|67[hw]|7[045][bh]|71[hk]|73o|76[ho]|79w|81[hks]?|82h|90[bhsy]|99b)|810[hs]))(_\w(\w|\w\w))?(\)| bui)/i + ], [MODEL, [VENDOR, 'TCL'], [TYPE, MOBILE]], [ // Acer /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 0f11ef3d7..40b84b129 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2420,6 +2420,51 @@ "type": "smarttv" } }, + { + "desc": "TCL 10 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T790Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 EdgA/114.0.1823.43", + "expect": { + "vendor": "TCL", + "model": "T790Y", + "type": "mobile" + } + }, + { + "desc": "TCL 10 5G UW", + "ua": "Mozilla/5.0 (Linux; Android 10; T790S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T790S", + "type": "mobile" + } + }, + { + "desc": "TCL 10 Plus", + "ua": "Mozilla/5.0 (Linux; Android 11; T782H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36 OPR/64.3.3282.60839", + "expect": { + "vendor": "TCL", + "model": "T782H", + "type": "mobile" + } + }, + { + "desc": "TCL 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; T799B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T799B", + "type": "mobile" + } + }, + { + "desc": "TCL 10 SE", + "ua": "Mozilla/5.0 (Linux; Android 10; T766H_RU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T766H", + "type": "mobile" + } + }, { "desc": "TCL 10 TabMax", "ua": "Mozilla/5.0 (Linux; Android 11; 9296Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", @@ -2447,6 +2492,312 @@ "type": "tablet" } }, + { + "desc": "TCL 10L", + "ua": "Mozilla/5.0 (Linux; Android 10; T770B Build/QKQ1.200329.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 GSA/11.41.10.23.arm64", + "expect": { + "vendor": "TCL", + "model": "T770B", + "type": "mobile" + } + }, + { + "desc": "TCL 10L", + "ua": "Mozilla/5.0 (Linux; Android 11; T770H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T770H", + "type": "mobile" + } + }, + { + "desc": "TCL 20 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T781) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T781", + "type": "mobile" + } + }, + { + "desc": "TCL 20 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T810S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36 EdgA/113.0.1774.63", + "expect": { + "vendor": "TCL", + "model": "T810S", + "type": "mobile" + } + }, + { + "desc": "TCL 20 SE", + "ua": "Mozilla/5.0 (Linux; Android 11; T671H Build/RKQ1.201112.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.73 Mobile Safari/537.36 GoogleApp/13.9.7.23.arm64", + "expect": { + "vendor": "TCL", + "model": "T671H", + "type": "mobile" + } + }, + { + "desc": "TCL 20 XE", + "ua": "Mozilla/5.0 (Linux; Android 11; 5087Z) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "5087Z", + "type": "mobile" + } + }, + { + "desc": "TCL 20B", + "ua": "Mozilla/5.0 (Linux; Android 11; 6159K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6159K", + "type": "mobile" + } + }, + { + "desc": "TCL 205", + "ua": "Mozilla/5.0 (Linux; Android 11; 4187D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "4187D", + "type": "mobile" + } + }, + { + "desc": "TCL 20E", + "ua": "Mozilla/5.0 (Linux; Android 11; 6125A) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/18.0 Chrome/99.0.4844.88 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6125A", + "type": "mobile" + } + }, + { + "desc": "TCL 20L", + "ua": "Mozilla/5.0 (Linux; Android 11; T774H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.59 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T774H", + "type": "mobile" + } + }, + { + "desc": "TCL 20L Plus", + "ua": "Mozilla/5.0 (Linux; Android 11; T775H Build/RKQ1.210107.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.61 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T775H", + "type": "mobile" + } + }, + { + "desc": "TCL 20R 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T767H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36 OPR/71.3.3718.67322", + "expect": { + "vendor": "TCL", + "model": "T767H", + "type": "mobile" + } + }, + { + "desc": "TCL 20S", + "ua": "Mozilla/5.0 (Linux; Android 11; T773O) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T773O", + "type": "mobile" + } + }, + { + "desc": "TCL 20Y", + "ua": "Mozilla/5.0 (Linux; Android 11; 6156D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.134 Mobile Safari/537.36 OPR/70.3.3653.66287", + "expect": { + "vendor": "TCL", + "model": "6156D", + "type": "mobile" + } + }, + { + "desc": "TCL 30 V 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T781S Build/RKQ1.210614.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/369.0.0.5.110;]", + "expect": { + "vendor": "TCL", + "model": "T781S", + "type": "mobile" + } + }, + { + "desc": "TCL 30 XE 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T767W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/416.0.0.35.85;]", + "expect": { + "vendor": "TCL", + "model": "T767W", + "type": "mobile" + } + }, + { + "desc": "TCL 305", + "ua": "Mozilla/5.0 (Linux; arm; Android 11; 6102D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.167 YaBrowser/22.7.6.96.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6102D", + "type": "mobile" + } + }, + { + "desc": "TCL 306", + "ua": "Mozilla/5.0 (Linux; Android 12; 6102H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.141 Mobile Safari/537.36[FBAN/EMA;FBLC/it_IT;FBAV/332.0.0.22.108;]", + "expect": { + "vendor": "TCL", + "model": "6102H", + "type": "mobile" + } + }, + { + "desc": "TCL 30", + "ua": "Mozilla/5.0 (Linux; Android 12; T676H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T676H", + "type": "mobile" + } + }, + { + "desc": "TCL 30+", + "ua": "Mozilla/5.0 (Linux; Android 12; T676J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T676J", + "type": "mobile" + } + }, + { + "desc": "TCL 30 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T776H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T776H", + "type": "mobile" + } + }, + { + "desc": "TCL 30 LE", + "ua": "Mozilla/5.0 (Linux; Android 12; 4188V Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/352.0.0.14.108;]", + "expect": { + "vendor": "TCL", + "model": "4188V", + "type": "mobile" + } + }, + { + "desc": "TCL 30 SE", + "ua": "Mozilla/5.0 (Linux; Android 12; 6165H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/396.1.0.28.104;]", + "expect": { + "vendor": "TCL", + "model": "6165H", + "type": "mobile" + } + }, + { + "desc": "TCL 30E", + "ua": "Mozilla/5.0 (Linux; Android 12; 6127I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6127I", + "type": "mobile" + } + }, + { + "desc": "TCL 40 NxtPaper", + "ua": "Mozilla/5.0 (Linux; Android 13; T612B Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.53 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T612B", + "type": "mobile" + } + }, + { + "desc": "TCL A3", + "ua": "Mozilla/5.0 (Linux; Android 11; A509DL Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 GSA/13.18.7.23.arm64", + "expect": { + "vendor": "TCL", + "model": "A509DL", + "type": "mobile" + } + }, + { + "desc": "TCL A30", + "ua": "Mozilla/5.0 (Linux; Android 11; 5102L Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/413.0.0.30.104;]", + "expect": { + "vendor": "TCL", + "model": "5102L", + "type": "mobile" + } + }, + { + "desc": "TCL 40 SE", + "ua": "Mozilla/5.0 (Linux; Android 13; T610K Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T610K", + "type": "mobile" + } + }, + { + "desc": "TCL 40 XE 5G", + "ua": "Mozilla/5.0 (Linux; Android 13; T609DL Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.136 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/466.1.0.57.85;]", + "expect": { + "vendor": "TCL", + "model": "T609DL", + "type": "mobile" + } + }, + { + "desc": "TCL 403", + "ua": "Mozilla/5.0 (Linux; Android 12; T431D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T431D", + "type": "mobile" + } + }, + { + "desc": "TCL 405", + "ua": "Mozilla/5.0 (Linux; Android 12; T506D Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/418.0.0.33.69;]", + "expect": { + "vendor": "TCL", + "model": "T506D", + "type": "mobile" + } + }, + { + "desc": "TCL 408", + "ua": "Mozilla/5.0 (Linux; U; Android 12; T507U Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/105.0.5195.136 Mobile Safari/537.36 OPR/75.0.2254.68857", + "expect": { + "vendor": "TCL", + "model": "T507U", + "type": "mobile" + } + }, + { + "desc": "TCL 40R 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T771K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 EdgA/114.0.1823.37", + "expect": { + "vendor": "TCL", + "model": "T771K", + "type": "mobile" + } + }, + { + "desc": "TCL Ion X", + "ua": "Mozilla/5.0 (Linux; Android 12; T430W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.60 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T430W", + "type": "mobile" + } + }, { "desc": "TCL NxtPaper 11", "ua": "Mozilla/5.0 (Linux; Android 13; 9466X Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.179 Safari/537.36 [FB_IAB/FB4A;FBAV/473.0.0.41.81;]", @@ -2456,6 +2807,15 @@ "type": "tablet" } }, + { + "desc": "TCL Stylus 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T779W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.2 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T779W", + "type": "mobile" + } + }, { "desc": "TCL Tab 8 4G", "ua": "Mozilla/5.0 (Linux; Android 10; 9048S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", @@ -2528,6 +2888,15 @@ "type": "tablet" } }, + { + "desc": "TCL Xess P17AA", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TCL Xess P17AA Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "Xess P17AA", + "type": "tablet" + } + }, { "desc": "Tecno KC8", "ua": "Mozilla/5.0 (Linux; Android 10; TECNO KC8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", From 692b175d49b74d1c93e14e49daa9b36c12de221d Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Tue, 13 Aug 2024 20:46:21 -0700 Subject: [PATCH 224/388] feat: Update and expand Chromecast device families (#724) --- src/main/ua-parser.js | 27 ++++++++++++++++++++++----- test/specs/device-all.json | 36 ++++++++++++++++++++++++++++++++++++ test/specs/os-all.json | 31 +++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b817bdf01..a8ba868d0 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -82,6 +82,7 @@ PREFIX_MOBILE = 'Mobile ', SUFFIX_BROWSER = ' Browser', CHROME = 'Chrome', + CHROMECAST = 'Chromecast', EDGE = 'Edge', FIREFOX = 'Firefox', OPERA = 'Opera', @@ -696,8 +697,14 @@ ], [[VENDOR, LG], [TYPE, SMARTTV]], [ /(apple) ?tv/i // Apple TV ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ - /crkey/i // Google Chromecast - ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey.*devicetype\/chromecast/i // Google Chromecast Third Generation + ], [[MODEL, CHROMECAST+' Third Generation'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey.*devicetype\/([^/]*)/i // Google Chromecast with specific device type + ], [[MODEL, /^/, 'Chromecast '], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /fuchsia.*crkey/i // Google Chromecast Nest Hub + ], [[MODEL, CHROMECAST+' Nest Hub'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey/i // Google Chromecast, Linux-based or unknown + ], [[MODEL, CHROMECAST], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ /\(dtv[\);].+(aquos)/i, @@ -817,6 +824,18 @@ /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ + // Google Chromecast + /android ([\d\.]+).*crkey/i // Google Chromecast, Android-based + ], [VERSION, [NAME, CHROMECAST + ' Android']], [ + /fuchsia.*crkey\/([\d\.]+)/i // Google Chromecast, Fuchsia-based + ], [VERSION, [NAME, CHROMECAST + ' Fuchsia']], [ + /crkey\/([\d\.]+).*devicetype\/smartspeaker/i // Google Chromecast, Linux-based Smart Speaker + ], [VERSION, [NAME, CHROMECAST + ' SmartSpeaker']], [ + /linux.*crkey\/([\d\.]+)/i // Google Chromecast, Legacy Linux-based + ], [VERSION, [NAME, CHROMECAST + ' Linux']], [ + /crkey\/([\d\.]+)/i // Google Chromecast, unknown + ], [VERSION, [NAME, CHROMECAST]], [ + // Mobile OSes /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS @@ -837,9 +856,7 @@ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS ], [VERSION, [NAME, 'watchOS']], [ - // Google Chromecast - /crkey\/([\d\.]+)/i // Google Chromecast - ], [VERSION, [NAME, CHROME+'cast']], [ + // Google ChromeOS /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS ], [[NAME, "Chrome OS"], VERSION],[ diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 40b84b129..aa3c8f027 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -3554,6 +3554,42 @@ "type": "mobile" } }, + { + "desc": "Google Chromecast with Google TV", + "ua": "Mozilla/5.0 (Linux; Android 12.0; Build/STTL.240206.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.0 Safari/537.36 CrKey/1.56.500000 DeviceType/AndroidTV", + "expect": { + "vendor": "Google", + "model": "Chromecast AndroidTV", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Mini Smart Speaker", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/SmartSpeaker", + "expect": { + "vendor": "Google", + "model": "Chromecast SmartSpeaker", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Third Generation", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/Chromecast", + "expect": { + "vendor": "Google", + "model": "Chromecast Third Generation", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Nest Hub", + "ua": "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 CrKey/1.56.500000", + "expect": { + "vendor": "Google", + "model": "Chromecast Nest Hub", + "type": "smarttv" + } + }, { "desc": "Google Chromecast", "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.84 Safari/537.36 CrKey/1.22.79313", diff --git a/test/specs/os-all.json b/test/specs/os-all.json index 5e9beb288..6fc03ecdd 100644 --- a/test/specs/os-all.json +++ b/test/specs/os-all.json @@ -414,11 +414,38 @@ } }, { - "desc" : "Google Chromecast", + "desc" : "Google Chromecast with Google TV", + "ua" : "Mozilla/5.0 (Linux; Android 12.0; Build/STTL.240206.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.0 Safari/537.36 CrKey/1.56.500000 DeviceType/AndroidTV", + "expect" : + { + "name" : "Chromecast Android", + "version" : "12.0" + } + }, + { + "desc" : "Google Chromecast Nest Hub", + "ua" : "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 CrKey/1.56.500000", + "expect" : + { + "name" : "Chromecast Fuchsia", + "version" : "1.56.500000" + } + }, + { + "desc" : "Google Chromecast Mini Smart Speaker", + "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/SmartSpeaker", + "expect" : + { + "name" : "Chromecast SmartSpeaker", + "version" : "1.56.500000" + } + }, + { + "desc" : "Google Chromecast Legacy Linux-Based", "ua" : "Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.81 Safari/537.36 CrKey/1.42.183786", "expect" : { - "name" : "Chromecast", + "name" : "Chromecast Linux", "version" : "1.42.183786" } }, From 68ae2a76deaa9cf2826b375b25d96fa4d14dd044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Os=C3=A9s?= Date: Wed, 14 Aug 2024 00:46:46 -0300 Subject: [PATCH 225/388] adding detection of MJ12Bot and SemrushBot into extension Crawlers including testing (#738) --- src/extensions/ua-parser-extensions.js | 4 +++- src/extensions/ua-parser-extensions.mjs | 4 +++- test/specs/browser-crawlers.json | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index bd0ee4056..f0d4a7b8c 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -46,7 +46,9 @@ const Crawlers = Object.freeze({ // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot - /((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i, + // MJ12bot - https://mj12bot.com/ + // SemrushBot - http://www.semrush.com/bot.html + /((?:amazon|apple|bing|duckduck|facebook|gpt|mj12|semrush)bot)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 9bec89023..192889056 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -48,7 +48,9 @@ const Crawlers = Object.freeze({ // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot - [/((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i], + // MJ12bot - https://mj12bot.com/ + // SemrushBot - http://www.semrush.com/bot.html + [/((?:amazon|apple|bing|duckduck|facebook|gpt|mj12|semrush)bot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, CRAWLER]], // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json index 107ab24b4..b7e8169b2 100644 --- a/test/specs/browser-crawlers.json +++ b/test/specs/browser-crawlers.json @@ -79,6 +79,26 @@ "type" : "crawler" } }, + { + "desc" : "MJ12bot", + "ua" : "Mozilla/5.0 (compatible; MJ12bot/v1.4.8; http://mj12bot.com/)", + "expect" : + { + "name" : "MJ12bot", + "version" : "v1.4.8", + "type" : "crawler" + } + }, + { + "desc" : "SemrushBot", + "ua" : "Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)", + "expect" : + { + "name" : "SemrushBot", + "version" : "7", + "type" : "crawler" + } + }, { "desc" : "Yahoo! Japan", "ua" : "Y!J-BRW/1.0 (https://www.yahoo-help.jp/app/answers/detail/p/595/a_id/42716)", From ccc554232bc29df4bea424ac89a6f7f268ca3bdc Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 16 Aug 2024 13:36:05 +0700 Subject: [PATCH 226/388] Improve detection: recognize Samsung Galaxy Watch devices as `wearable` --- src/main/ua-parser.js | 6 ++++-- test/specs/device-all.json | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a8ba868d0..a6cbfcd19 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -500,8 +500,8 @@ // Samsung /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [ - /\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, - /samsung[- ]([-\w]+)/i, + /\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, + /samsung[- ]((?!sm-[lr])[-\w]+)/i, /sec-(sgh\w+)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ @@ -740,6 +740,8 @@ // WEARABLES /////////////////// + /\b(sm-[lr]\d\d[05][fnuw]?s?)\b/i // Samsung Galaxy Watch + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [ /((pebble))app/i // Pebble ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch diff --git a/test/specs/device-all.json b/test/specs/device-all.json index aa3c8f027..bff516361 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2132,6 +2132,42 @@ "type": "tablet" } }, + { + "desc": "Samsung Galaxy Watch", + "ua": "Mozilla/5.0 (Linux; Tizen 5.5; SAMSUNG SM-R805W) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.0 Chrome/69.0.3497.106 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R805W", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch Active 2", + "ua": "Mozilla/5.0 (Linux; Tizen 5.5; SAMSUNG SM-R820) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.0 Chrome/69.0.3497.106 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R820", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch4", + "ua": "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-R875U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.2. Chrome/102.0.5005.125 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R875U", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-R925U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.2. Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R925U", + "type": "wearable" + } + }, { "desc": "Samsung Note 10.1", "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-P605) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", From b8d823dd574411906028c4ae3e4bd1bcc5ee936c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 16 Aug 2024 14:23:07 +0700 Subject: [PATCH 227/388] Improve detection: Amazon Echo Show devices --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a6cbfcd19..75ff935c1 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -600,7 +600,7 @@ // Amazon /(alexa)webm/i, - /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i, // Kindle Fire without Silk / Echo Show + /(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [ /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone diff --git a/test/specs/device-all.json b/test/specs/device-all.json index bff516361..125555ab8 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1916,6 +1916,24 @@ "type": "tablet" } }, + { + "desc": "Echo Show 8", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; AEOCW) AppleWebKit/537.36 (KHTML, like Gecko) Silk/106.3.3 like Chrome/106.0.5249.170 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOCW", + "type": "tablet" + } + }, + { + "desc": "Echo Show 15", + "ua": "Mozilla/5.0 (Linux; Android 9; AEOHY) AppleWebKit/537.36 (KHTML, like Gecko) Silk/112.6.3 like Chrome/112.0.5615.213 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOHY", + "type": "tablet" + } + }, { "desc": "Echo Dot", "ua": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEOBC Build/LVY48F)", From b1dae13245189dd3c56dbd5ae8fd67eca55496fb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 19 Aug 2024 11:10:15 +0700 Subject: [PATCH 228/388] [extension][bot] Add AhrefsBot, AhrefsSiteAudit, Dotbot, Rogerbot, Uptimerobot, Coc Coc Bot --- src/extensions/ua-parser-extensions.js | 12 ++++++-- test/specs/browser-crawlers.json | 40 ++++++++++++++++++++++++++ test/specs/browser-fetchers.json | 30 +++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index f0d4a7b8c..a85052c37 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -40,15 +40,17 @@ const CLIs = Object.freeze({ const Crawlers = Object.freeze({ browser : [ [ + // AhrefsBot - https://ahrefs.com/robot // Amazonbot - https://developer.amazon.com/amazonbot // Applebot - http://apple.com/go/applebot // Bingbot - http://www.bing.com/bingbot.htm + // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot // MJ12bot - https://mj12bot.com/ // SemrushBot - http://www.semrush.com/bot.html - /((?:amazon|apple|bing|duckduck|facebook|gpt|mj12|semrush)bot)\/([\w\.]+)/i, + /((?:ahrefs|amazon|apple|bing|dot|duckduck|facebook|gpt|mj12|semrush)bot)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, @@ -56,6 +58,9 @@ const Crawlers = Object.freeze({ // ClaudeBot /(claude(?:bot|-web))\/([\w\.]+)/i, + // Coc Coc Bot - https://help.coccoc.com/en/search-engine + /(coccocbot-(?:image|web))\/([\w\.]+)/i, + // Googlebot - http://www.google.com/bot.html /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i, @@ -176,9 +181,10 @@ const Emails = Object.freeze({ const Fetchers = Object.freeze({ browser : [ [ + // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit // ChatGPT-User - https://platform.openai.com/docs/plugins/bot - // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot - /(bingpreview|chatgpt-user|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i, + // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / Telegrambot / Twitterbot / UptimeRobot + /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot)\/([\w\.]+)/i, // Slackbot - https://api.slack.com/robots /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json index b7e8169b2..b205ebf83 100644 --- a/test/specs/browser-crawlers.json +++ b/test/specs/browser-crawlers.json @@ -1,4 +1,14 @@ [ + { + "desc" : "AhrefsBot", + "ua" : "Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)", + "expect" : + { + "name" : "AhrefsBot", + "version" : "7.0", + "type" : "crawler" + } + }, { "desc" : "Applebot", "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)", @@ -39,6 +49,26 @@ "type" : "crawler" } }, + { + "desc" : "Coc Coc Bot (web)", + "ua" : "Mozilla/5.0 (compatible; coccocbot-web/1.0; +http://help.coccoc.com/searchengine)", + "expect" : + { + "name" : "coccocbot-web", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Coc Coc Bot (image)", + "ua" : "Mozilla/5.0 (compatible; coccocbot-image/1.0; +http://help.coccoc.com/searchengine)", + "expect" : + { + "name" : "coccocbot-image", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "ClaudeWeb", "ua" : "Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)", @@ -49,6 +79,16 @@ "type" : "crawler" } }, + { + "desc" : "Dotbot", + "ua" : "Mozilla/5.0 (compatible; DotBot/1.2; +https://opensiteexplorer.org/dotbot; help@moz.com)", + "expect" : + { + "name" : "DotBot", + "version" : "1.2", + "type" : "crawler" + } + }, { "desc" : "FacebookBot", "ua" : "Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/", diff --git a/test/specs/browser-fetchers.json b/test/specs/browser-fetchers.json index 6b537728f..c508bd22f 100644 --- a/test/specs/browser-fetchers.json +++ b/test/specs/browser-fetchers.json @@ -1,4 +1,14 @@ [ + { + "desc" : "AhrefsSiteAudit", + "ua" : "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Mobile Safari/537.36 (compatible; AhrefsSiteAudit/6.1; +http://ahrefs.com/robot/site-audit)", + "expect" : + { + "name" : "AhrefsSiteAudit", + "version" : "6.1", + "type" : "fetcher" + } + }, { "desc" : "BingPreview", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b", @@ -18,5 +28,25 @@ "version" : "1.0", "type" : "fetcher" } + }, + { + "desc" : "Rogerbot", + "ua" : "Mozilla/5.0 (compatible; rogerBot/1.0; UrlCrawler; http://www.seomoz.org/dp/rogerbot)", + "expect" : + { + "name" : "rogerBot", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "UptimeRobot", + "ua" : "Mozilla/5.0 (compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)", + "expect" : + { + "name" : "UptimeRobot", + "version" : "2.0", + "type" : "fetcher" + } } ] \ No newline at end of file From 491c2d7477b8536b332ee6e1a9af5744129e3c9a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 24 Aug 2024 10:38:09 +0700 Subject: [PATCH 229/388] Add new device vendor: itel https://www.itel-life.com/ --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 4 +++ test/specs/device-all.json | 54 ++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 3801a86e1..e8c638efb 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -209,6 +209,7 @@ const Vendor = Object.freeze({ HTC: 'HTC', HUAWEI: 'Huawei', INFINIX: 'Infinix', + ITEL: 'itel', JOLLA: 'Jolla', KOBO: 'Kobo', LENOVO: 'Lenovo', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 75ff935c1..2a729e74c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -636,6 +636,10 @@ /droid [\w\.]+; (418(?:7d|8v)|5087z|5102l|61(?:02[dh]|25[adfh]|27[ai]|56[dh]|59k|65[ah])|a509dl|t(?:43(?:0w|1[adepqu])|50(?:6d|7[adju])|6(?:09dl|10k|12b|71[efho]|76[hjk])|7(?:66[ahju]|67[hw]|7[045][bh]|71[hk]|73o|76[ho]|79w|81[hks]?|82h|90[bhsy]|99b)|810[hs]))(_\w(\w|\w\w))?(\)| bui)/i ], [MODEL, [VENDOR, 'TCL'], [TYPE, MOBILE]], [ + // itel + /(itel) ((\w+))/i + ], [[VENDOR, lowerize], MODEL, [TYPE, strMapper, { 'tablet' : ['p10001l', 'w7001'], '*' : 'mobile' }]], [ + // Acer /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [ diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 125555ab8..03340a2d9 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1286,6 +1286,60 @@ "type": "mobile" } }, + { + "desc": "itel A25", + "ua": "Mozilla/5.0 (Linux; Android 9; itel L5002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.130 Mobile Safari/537.36 OPR/63.3.3216.58675", + "expect": { + "vendor": "itel", + "model": "L5002", + "type": "mobile" + } + }, + { + "desc": "itel A50", + "ua": "Mozilla/5.0 (Linux; U; Android 14; itel A667L Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Mobile Safari/537.36 OPR/83.1.2254.73239", + "expect": { + "vendor": "itel", + "model": "A667L", + "type": "mobile" + } + }, + { + "desc": "itel KidPad 1", + "ua": "Mozilla/5.0 (Linux; Android 10; Itel W7001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.101 Mobile Safari/537.36", + "expect": { + "vendor": "itel", + "model": "W7001", + "type": "tablet" + } + }, + { + "desc": "itel Pad One", + "ua": "Mozilla/5.0 (Linux; Android 12; itel P10001L Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.172 Safari/537.36", + "expect": { + "vendor": "itel", + "model": "P10001L", + "type": "tablet" + } + }, + { + "desc": "itel RS4", + "ua": "Mozilla/5.0 (Linux; Android 13; itel S666LN Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.165 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/468.1.0.56.78;]", + "expect": { + "vendor": "itel", + "model": "S666LN", + "type": "mobile" + } + }, + { + "desc": "itel Vision 2S", + "ua": "Mozilla/5.0 (Linux; Android 11; itel P651L Build/RP1A.201005.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 Mobile Safari/537.36", + "expect": { + "vendor": "itel", + "model": "P651L", + "type": "mobile" + } + }, { "desc": "Moto X", "ua": "Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", From b9f1bf6223f0470b316449959f08210e12dc474f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 6 Sep 2024 19:26:05 +0700 Subject: [PATCH 230/388] Fix #743 - Improve device detection for Xiaomi --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 2a729e74c..36c9e787c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -532,7 +532,7 @@ /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models - /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi + /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i // Xiaomi Mi ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 03340a2d9..c85860024 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -3230,6 +3230,15 @@ "type": "mobile" } }, + { + "desc": "Xiaomi Mi 10 Pro", + "ua": "Linux; U; Android 13; Mi 10 Pro Build/TKQ1.221114.001", + "expect": { + "vendor": "Xiaomi", + "model": "Mi 10 Pro", + "type": "mobile" + } + }, { "desc": "Xiaomi Mi 5s Plus", "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; MI 5s Plus Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/8.7.1", From db1612401f1d135b49b12be185d46f121f50af48 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 14 Sep 2024 12:38:51 +0700 Subject: [PATCH 231/388] Add new helper method: `isFromEU()` to detect whether user comes from an EU country --- package-lock.json | 22 ++++++++++++++++++++++ package.json | 3 +++ script/build-esm.js | 3 ++- src/helpers/ua-parser-helpers.d.ts | 2 ++ src/helpers/ua-parser-helpers.js | 2 ++ 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 71498414b..c1e325174 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,9 @@ } ], "license": "AGPL-3.0-or-later", + "dependencies": { + "detect-europe-js": "^0.1.1" + }, "bin": { "ua-parser-js": "script/cli.js" }, @@ -1664,6 +1667,25 @@ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true }, + "node_modules/detect-europe-js": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.1.tgz", + "integrity": "sha512-+bUXDf+tI3L4dcEuRdAFa44Amx9aEaJzoZssx7Xis4H1bXWc5fAcOP850BOj0wJPRzOdovOuOVEvrg6T+GflZA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ] + }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", diff --git a/package.json b/package.json index 618a794d8..6a7d31ac2 100755 --- a/package.json +++ b/package.json @@ -210,6 +210,9 @@ "test:mocha": "mocha test/mocha*js", "test:playwright": "playwright test" }, + "dependencies": { + "detect-europe-js": "^0.1.1" + }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.23.2", diff --git a/script/build-esm.js b/script/build-esm.js index cd7523878..68b8258a0 100755 --- a/script/build-esm.js +++ b/script/build-esm.js @@ -7,7 +7,8 @@ const generateMJS = (module) => { let text = fs.readFileSync(src, 'utf-8'); replacements.push( - [/const (.+?)\s*=\s*require\(\'(.+)\'\)/ig, 'import $1 from \'$2.mjs\''], + [/const (.+?)\s*=\s*require\(\'\.(.+)\'\)/ig, 'import $1 from \'$2.mjs\''], + [/const (.+?)\s*=\s*require\(\'(.+)\'\)/ig, 'import $1 from \'$2\''], [/module\.exports =/ig, 'export'] ); replacements.forEach(rep => { diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index c58fc1463..6a9765830 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -7,6 +7,7 @@ import { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; declare function isAppleSilicon(res: IResult): boolean; declare function isChromeFamily(res: IResult): boolean; +declare function isFromEU(): boolean; declare function isFrozenUA(ua: string): boolean; declare function isStandalonePWA(): boolean; @@ -14,6 +15,7 @@ export { getDeviceVendor, isAppleSilicon, isChromeFamily, + isFromEU, isFrozenUA, isStandalonePWA } \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index e0729c94d..72116124f 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -9,6 +9,7 @@ const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); const { UAParser } = require('../main/ua-parser'); +const { isFromEU } = require('detect-europe-js'); const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; @@ -50,6 +51,7 @@ module.exports = { getDeviceVendor, isAppleSilicon, isChromeFamily, + isFromEU, isFrozenUA, isStandalonePWA } \ No newline at end of file From 3de1961892af54985e991ee356453e300ab57e75 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 14 Sep 2024 13:13:36 +0700 Subject: [PATCH 232/388] Add new helper method: `isElectron()` to detect whether the current window is running inside Electron --- src/helpers/ua-parser-helpers.d.ts | 2 ++ src/helpers/ua-parser-helpers.js | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 6a9765830..e8dd9d358 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -7,6 +7,7 @@ import { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; declare function isAppleSilicon(res: IResult): boolean; declare function isChromeFamily(res: IResult): boolean; +declare function isElectron(): boolean; declare function isFromEU(): boolean; declare function isFrozenUA(ua: string): boolean; declare function isStandalonePWA(): boolean; @@ -15,6 +16,7 @@ export { getDeviceVendor, isAppleSilicon, isChromeFamily, + isElectron, isFromEU, isFrozenUA, isStandalonePWA diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 72116124f..8582fe5b4 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -35,6 +35,9 @@ const isAppleSilicon = (res) => { const isChromeFamily = (res) => res.engine.is(Engine.BLINK); +const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js + / electron\//i.test(navigator?.userAgent)); // browser + const isFrozenUA = (ua) => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); const isStandalonePWA = () => window && (window.matchMedia('(display-mode: standalone)').matches || @@ -51,6 +54,7 @@ module.exports = { getDeviceVendor, isAppleSilicon, isChromeFamily, + isElectron, isFromEU, isFrozenUA, isStandalonePWA From 391b8087fbfcad6fd2e763dc5159aabcfe805b2a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 20 Sep 2024 09:26:39 +0700 Subject: [PATCH 233/388] Fix #747: Python Request mistakenly identified as Meta Quest --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 36c9e787c..59ac6553d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -761,7 +761,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ /(pico) (4|neo3(?: link|pro)?)/i // Pico ], [VENDOR, MODEL, [TYPE, XR]], [ - /(quest( \d| pro)?)/i // Oculus Quest + /; (quest( \d| pro)?)/i // Oculus Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ /////////////////// diff --git a/test/specs/device-all.json b/test/specs/device-all.json index c85860024..d8ef0bb91 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -1484,6 +1484,15 @@ "type": "xr" } }, + { + "desc": "Issue #747", + "ua": "python-requests/2.25.1", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "undefined" + } + }, { "desc": "OnePlus One", "ua": "Mozilla/5.0 (Linux; Android 4.4.4; A0001 Build/KTU84Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.59 Mobile Safari/537.36", From f00fb3a2e239722d123edf811754e8cb918a6ed7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 22 Sep 2024 13:46:17 +0700 Subject: [PATCH 234/388] [submodule:extensions] Restore `Bots` as an umbrella term for any kind of automated browsers: `CLIs`, `Crawlers`, `Fetchers`, and `Modules` --- src/extensions/ua-parser-extensions.d.ts | 1 + src/extensions/ua-parser-extensions.js | 14 ++++++++++++++ test/mocha-test-extension.js | 6 ++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 5d6f300de..e432b21f6 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -4,6 +4,7 @@ import type { UAParserExt } from "../main/ua-parser"; +export const Bots: UAParserExt; export const CLIs: UAParserExt; export const Crawlers: UAParserExt; export const ExtraDevices: UAParserExt; diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index a85052c37..a1efa2491 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -342,7 +342,21 @@ const Modules = Object.freeze({ ] }); +////////// +// BOTS +///////// + +const Bots = Object.freeze({ + browser : [ + ...CLIs.browser, + ...Crawlers.browser, + ...Fetchers.browser, + ...Modules.browser + ] +}); + module.exports = { + Bots, CLIs, Crawlers, ExtraDevices, diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index c2ee99d89..2f81a5c2f 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -9,7 +9,7 @@ const crawlers = require('./specs/browser-crawlers.json'); const emails = require('./specs/browser-emails.json'); const fetchers = require('./specs/browser-fetchers.json'); const modules = require('./specs/browser-modules.json'); -const { CLIs, Crawlers, Emails, Fetchers, Modules } = require('../src/extensions/ua-parser-extensions'); +const { Bots, CLIs, Crawlers, Emails, Fetchers, Modules } = require('../src/extensions/ua-parser-extensions'); describe('Extensions', () => { [ @@ -38,6 +38,8 @@ describe('Extensions', () => { const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; + assert.equal(UAParser(scrapy, Bots).browser.name, 'Scrapy'); + const emailParser = new UAParser(Emails); assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); @@ -67,7 +69,7 @@ describe('Merge', () => { }); }); -describe('Testing regexes', () => { +describe('Testing the safety of regexes', () => { let regexes; let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString(); From a4c81a5600e8fb30515b8375e24463b5acedaec8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 28 Sep 2024 10:29:40 +0700 Subject: [PATCH 235/388] [submodule:extensions] Add new crawler: OAI-SearchBot (OpenAI's bot for SearchGPT) --- src/extensions/ua-parser-extensions.js | 3 ++- test/specs/browser-crawlers.json | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index a1efa2491..a50145daf 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -49,8 +49,9 @@ const Crawlers = Object.freeze({ // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot // MJ12bot - https://mj12bot.com/ + // OpenAI Search - https://platform.openai.com/docs/bots // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|apple|bing|dot|duckduck|facebook|gpt|mj12|semrush)bot)\/([\w\.]+)/i, + /((?:ahrefs|amazon|apple|bing|dot|duckduck|facebook|gpt|mj12|oai-search|semrush)bot)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json index b205ebf83..45e25ad18 100644 --- a/test/specs/browser-crawlers.json +++ b/test/specs/browser-crawlers.json @@ -129,6 +129,16 @@ "type" : "crawler" } }, + { + "desc" : "OpenAI Search", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot", + "expect" : + { + "name" : "OAI-SearchBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "SemrushBot", "ua" : "Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)", From 5b7fe9141b67b3121ca1012accc34954012a13c0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 17 Oct 2024 15:43:06 +0700 Subject: [PATCH 236/388] [submodule:enums] Replace underscore with quotation --- src/enums/ua-parser-enums.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index e8c638efb..9f58b6158 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -8,8 +8,8 @@ /*jshint esversion: 6 */ const Browser = Object.freeze({ - _2345_EXPLORER: '2345Explorer', - _360: '360 Browser', + '2345_EXPLORER': '2345Explorer', + '360': '360 Browser', ALIPAY: 'Alipay', AMAYA: 'Amaya', ANDROID: 'Android Browser', @@ -158,6 +158,7 @@ const BrowserType = Object.freeze({ }); const CPU = Object.freeze({ + '68K': '68k', ARM : 'arm', ARM_64: 'arm64', ARM_HF: 'armhf', @@ -168,7 +169,6 @@ const CPU = Object.freeze({ IRIX_64: 'irix64', MIPS: 'mips', MIPS_64: 'mips64', - M68K: '68k', PA_RISC: 'pa-risc', PPC: 'ppc', SPARC: 'sparc', From 7201755f5fbe433fb5f1284da046b0f01e684f25 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 18 Oct 2024 21:34:07 +0700 Subject: [PATCH 237/388] Fix #719 - Add icons --- src/icons/mono/LICENSE.md | 3 +++ src/icons/mono/browser/alipay.svg | 1 + src/icons/mono/browser/android-browser.svg | 1 + src/icons/mono/browser/avast-secure-browser.svg | 1 + src/icons/mono/browser/baidu-browser.svg | 1 + src/icons/mono/browser/brave.svg | 1 + src/icons/mono/browser/chrome-headless.svg | 1 + src/icons/mono/browser/chrome-webview.svg | 1 + src/icons/mono/browser/chrome.svg | 1 + src/icons/mono/browser/chromium.svg | 1 + src/icons/mono/browser/duckduckgo.svg | 1 + src/icons/mono/browser/edge.svg | 1 + src/icons/mono/browser/electron.svg | 1 + src/icons/mono/browser/facebook.svg | 1 + src/icons/mono/browser/firefox-focus.svg | 1 + src/icons/mono/browser/firefox-reality.svg | 1 + src/icons/mono/browser/firefox.svg | 1 + src/icons/mono/browser/gsa.svg | 1 + src/icons/mono/browser/huawei-browser.svg | 1 + src/icons/mono/browser/icecat.svg | 1 + src/icons/mono/browser/ie.svg | 1 + src/icons/mono/browser/instagram.svg | 1 + src/icons/mono/browser/jasmine.svg | 1 + src/icons/mono/browser/kakaotalk.svg | 1 + src/icons/mono/browser/klarna.svg | 1 + src/icons/mono/browser/line.svg | 1 + src/icons/mono/browser/linkedin.svg | 1 + src/icons/mono/browser/miui-browser.svg | 1 + src/icons/mono/browser/mobile-chrome.svg | 1 + src/icons/mono/browser/mobile-firefox.svg | 1 + src/icons/mono/browser/mobile-safari.svg | 1 + src/icons/mono/browser/mozilla.svg | 1 + src/icons/mono/browser/naver.svg | 1 + src/icons/mono/browser/nokia-browser.svg | 1 + src/icons/mono/browser/oculus-browser.svg | 1 + src/icons/mono/browser/opera-coast.svg | 1 + src/icons/mono/browser/opera-gx.svg | 1 + src/icons/mono/browser/opera-mini.svg | 1 + src/icons/mono/browser/opera-mobi.svg | 1 + src/icons/mono/browser/opera-tablet.svg | 1 + src/icons/mono/browser/opera-touch.svg | 1 + src/icons/mono/browser/opera.svg | 1 + src/icons/mono/browser/qqbrowser.svg | 1 + src/icons/mono/browser/qqbrowserlite.svg | 1 + src/icons/mono/browser/safari.svg | 1 + src/icons/mono/browser/sailfish-browser.svg | 1 + src/icons/mono/browser/samsung-internet.svg | 1 + src/icons/mono/browser/smart-lenovo-browser.svg | 1 + src/icons/mono/browser/snapchat.svg | 1 + src/icons/mono/browser/sogou-explorer.svg | 1 + src/icons/mono/browser/sogou-mobile.svg | 1 + src/icons/mono/browser/tesla.svg | 1 + src/icons/mono/browser/tiktok.svg | 1 + src/icons/mono/browser/twitter.svg | 1 + src/icons/mono/browser/vivaldi.svg | 1 + src/icons/mono/browser/vivo-browser.svg | 1 + src/icons/mono/browser/wechat.svg | 1 + src/icons/mono/browser/weibo.svg | 1 + src/icons/mono/browser/yandex.svg | 1 + src/icons/mono/device/acer.svg | 1 + src/icons/mono/device/amazon.svg | 1 + src/icons/mono/device/apple.svg | 1 + src/icons/mono/device/asus.svg | 1 + src/icons/mono/device/att.svg | 1 + src/icons/mono/device/blackberry.svg | 1 + src/icons/mono/device/dell.svg | 1 + src/icons/mono/device/facebook.svg | 1 + src/icons/mono/device/fairphone.svg | 1 + src/icons/mono/device/google.svg | 1 + src/icons/mono/device/hp.svg | 1 + src/icons/mono/device/huawei.svg | 1 + src/icons/mono/device/lenovo.svg | 1 + src/icons/mono/device/lg.svg | 1 + src/icons/mono/device/meizu.svg | 1 + src/icons/mono/device/microsoft.svg | 1 + src/icons/mono/device/motorola.svg | 1 + src/icons/mono/device/nintendo.svg | 1 + src/icons/mono/device/nokia.svg | 1 + src/icons/mono/device/nvidia.svg | 1 + src/icons/mono/device/oneplus.svg | 1 + src/icons/mono/device/oppo.svg | 1 + src/icons/mono/device/panasonic.svg | 1 + src/icons/mono/device/roku.svg | 1 + src/icons/mono/device/samsung.svg | 1 + src/icons/mono/device/siemens.svg | 1 + src/icons/mono/device/sony.svg | 1 + src/icons/mono/device/tesla.svg | 1 + src/icons/mono/device/vivo.svg | 1 + src/icons/mono/device/vodafone.svg | 1 + src/icons/mono/device/xiaomi.svg | 1 + src/icons/mono/device/zebra.svg | 1 + src/icons/mono/os/android.svg | 1 + src/icons/mono/os/arch.svg | 1 + src/icons/mono/os/blackberry.svg | 1 + src/icons/mono/os/centos.svg | 1 + src/icons/mono/os/chrome_os.svg | 1 + src/icons/mono/os/chromecast.svg | 1 + src/icons/mono/os/debian.svg | 1 + src/icons/mono/os/deepin.svg | 1 + src/icons/mono/os/elementary_os.svg | 1 + src/icons/mono/os/fedora.svg | 1 + src/icons/mono/os/firefox.svg | 1 + src/icons/mono/os/freebsd.svg | 1 + src/icons/mono/os/gentoo.svg | 1 + src/icons/mono/os/gnu.svg | 1 + src/icons/mono/os/harmonyos.svg | 1 + src/icons/mono/os/ios.svg | 1 + src/icons/mono/os/kaios.svg | 1 + src/icons/mono/os/linux.svg | 1 + src/icons/mono/os/macos.svg | 1 + src/icons/mono/os/manjaro.svg | 1 + src/icons/mono/os/mint.svg | 1 + src/icons/mono/os/netbsd.svg | 1 + src/icons/mono/os/nintendo.svg | 1 + src/icons/mono/os/openbsd.svg | 1 + src/icons/mono/os/playstation.svg | 1 + src/icons/mono/os/raspbian.svg | 1 + src/icons/mono/os/redhat.svg | 1 + src/icons/mono/os/sailfish.svg | 1 + src/icons/mono/os/slackware.svg | 1 + src/icons/mono/os/suse.svg | 1 + src/icons/mono/os/ubuntu.svg | 1 + src/icons/mono/os/windows.svg | 1 + src/icons/mono/os/xbox.svg | 1 + src/icons/ua-parser-icons.js | 0 125 files changed, 126 insertions(+) create mode 100644 src/icons/mono/LICENSE.md create mode 100644 src/icons/mono/browser/alipay.svg create mode 100644 src/icons/mono/browser/android-browser.svg create mode 100644 src/icons/mono/browser/avast-secure-browser.svg create mode 100644 src/icons/mono/browser/baidu-browser.svg create mode 100644 src/icons/mono/browser/brave.svg create mode 100644 src/icons/mono/browser/chrome-headless.svg create mode 100644 src/icons/mono/browser/chrome-webview.svg create mode 100644 src/icons/mono/browser/chrome.svg create mode 100644 src/icons/mono/browser/chromium.svg create mode 100644 src/icons/mono/browser/duckduckgo.svg create mode 100644 src/icons/mono/browser/edge.svg create mode 100644 src/icons/mono/browser/electron.svg create mode 100644 src/icons/mono/browser/facebook.svg create mode 100644 src/icons/mono/browser/firefox-focus.svg create mode 100644 src/icons/mono/browser/firefox-reality.svg create mode 100644 src/icons/mono/browser/firefox.svg create mode 100644 src/icons/mono/browser/gsa.svg create mode 100644 src/icons/mono/browser/huawei-browser.svg create mode 100644 src/icons/mono/browser/icecat.svg create mode 100644 src/icons/mono/browser/ie.svg create mode 100644 src/icons/mono/browser/instagram.svg create mode 100644 src/icons/mono/browser/jasmine.svg create mode 100644 src/icons/mono/browser/kakaotalk.svg create mode 100644 src/icons/mono/browser/klarna.svg create mode 100644 src/icons/mono/browser/line.svg create mode 100644 src/icons/mono/browser/linkedin.svg create mode 100644 src/icons/mono/browser/miui-browser.svg create mode 100644 src/icons/mono/browser/mobile-chrome.svg create mode 100644 src/icons/mono/browser/mobile-firefox.svg create mode 100644 src/icons/mono/browser/mobile-safari.svg create mode 100644 src/icons/mono/browser/mozilla.svg create mode 100644 src/icons/mono/browser/naver.svg create mode 100644 src/icons/mono/browser/nokia-browser.svg create mode 100644 src/icons/mono/browser/oculus-browser.svg create mode 100644 src/icons/mono/browser/opera-coast.svg create mode 100644 src/icons/mono/browser/opera-gx.svg create mode 100644 src/icons/mono/browser/opera-mini.svg create mode 100644 src/icons/mono/browser/opera-mobi.svg create mode 100644 src/icons/mono/browser/opera-tablet.svg create mode 100644 src/icons/mono/browser/opera-touch.svg create mode 100644 src/icons/mono/browser/opera.svg create mode 100644 src/icons/mono/browser/qqbrowser.svg create mode 100644 src/icons/mono/browser/qqbrowserlite.svg create mode 100644 src/icons/mono/browser/safari.svg create mode 100644 src/icons/mono/browser/sailfish-browser.svg create mode 100644 src/icons/mono/browser/samsung-internet.svg create mode 100644 src/icons/mono/browser/smart-lenovo-browser.svg create mode 100644 src/icons/mono/browser/snapchat.svg create mode 100644 src/icons/mono/browser/sogou-explorer.svg create mode 100644 src/icons/mono/browser/sogou-mobile.svg create mode 100644 src/icons/mono/browser/tesla.svg create mode 100644 src/icons/mono/browser/tiktok.svg create mode 100644 src/icons/mono/browser/twitter.svg create mode 100644 src/icons/mono/browser/vivaldi.svg create mode 100644 src/icons/mono/browser/vivo-browser.svg create mode 100644 src/icons/mono/browser/wechat.svg create mode 100644 src/icons/mono/browser/weibo.svg create mode 100644 src/icons/mono/browser/yandex.svg create mode 100644 src/icons/mono/device/acer.svg create mode 100644 src/icons/mono/device/amazon.svg create mode 100644 src/icons/mono/device/apple.svg create mode 100644 src/icons/mono/device/asus.svg create mode 100644 src/icons/mono/device/att.svg create mode 100644 src/icons/mono/device/blackberry.svg create mode 100644 src/icons/mono/device/dell.svg create mode 100644 src/icons/mono/device/facebook.svg create mode 100644 src/icons/mono/device/fairphone.svg create mode 100644 src/icons/mono/device/google.svg create mode 100644 src/icons/mono/device/hp.svg create mode 100644 src/icons/mono/device/huawei.svg create mode 100644 src/icons/mono/device/lenovo.svg create mode 100644 src/icons/mono/device/lg.svg create mode 100644 src/icons/mono/device/meizu.svg create mode 100644 src/icons/mono/device/microsoft.svg create mode 100644 src/icons/mono/device/motorola.svg create mode 100644 src/icons/mono/device/nintendo.svg create mode 100644 src/icons/mono/device/nokia.svg create mode 100644 src/icons/mono/device/nvidia.svg create mode 100644 src/icons/mono/device/oneplus.svg create mode 100644 src/icons/mono/device/oppo.svg create mode 100644 src/icons/mono/device/panasonic.svg create mode 100644 src/icons/mono/device/roku.svg create mode 100644 src/icons/mono/device/samsung.svg create mode 100644 src/icons/mono/device/siemens.svg create mode 100644 src/icons/mono/device/sony.svg create mode 100644 src/icons/mono/device/tesla.svg create mode 100644 src/icons/mono/device/vivo.svg create mode 100644 src/icons/mono/device/vodafone.svg create mode 100644 src/icons/mono/device/xiaomi.svg create mode 100644 src/icons/mono/device/zebra.svg create mode 100644 src/icons/mono/os/android.svg create mode 100644 src/icons/mono/os/arch.svg create mode 100644 src/icons/mono/os/blackberry.svg create mode 100644 src/icons/mono/os/centos.svg create mode 100644 src/icons/mono/os/chrome_os.svg create mode 100644 src/icons/mono/os/chromecast.svg create mode 100644 src/icons/mono/os/debian.svg create mode 100644 src/icons/mono/os/deepin.svg create mode 100644 src/icons/mono/os/elementary_os.svg create mode 100644 src/icons/mono/os/fedora.svg create mode 100644 src/icons/mono/os/firefox.svg create mode 100644 src/icons/mono/os/freebsd.svg create mode 100644 src/icons/mono/os/gentoo.svg create mode 100644 src/icons/mono/os/gnu.svg create mode 100644 src/icons/mono/os/harmonyos.svg create mode 100644 src/icons/mono/os/ios.svg create mode 100644 src/icons/mono/os/kaios.svg create mode 100644 src/icons/mono/os/linux.svg create mode 100644 src/icons/mono/os/macos.svg create mode 100644 src/icons/mono/os/manjaro.svg create mode 100644 src/icons/mono/os/mint.svg create mode 100644 src/icons/mono/os/netbsd.svg create mode 100644 src/icons/mono/os/nintendo.svg create mode 100644 src/icons/mono/os/openbsd.svg create mode 100644 src/icons/mono/os/playstation.svg create mode 100644 src/icons/mono/os/raspbian.svg create mode 100644 src/icons/mono/os/redhat.svg create mode 100644 src/icons/mono/os/sailfish.svg create mode 100644 src/icons/mono/os/slackware.svg create mode 100644 src/icons/mono/os/suse.svg create mode 100644 src/icons/mono/os/ubuntu.svg create mode 100644 src/icons/mono/os/windows.svg create mode 100644 src/icons/mono/os/xbox.svg create mode 100644 src/icons/ua-parser-icons.js diff --git a/src/icons/mono/LICENSE.md b/src/icons/mono/LICENSE.md new file mode 100644 index 000000000..a4c806be3 --- /dev/null +++ b/src/icons/mono/LICENSE.md @@ -0,0 +1,3 @@ +CC0-1.0 +https://github.com/simple-icons/simple-icons +https://github.com/coreui/coreui-icons \ No newline at end of file diff --git a/src/icons/mono/browser/alipay.svg b/src/icons/mono/browser/alipay.svg new file mode 100644 index 000000000..30fa27f22 --- /dev/null +++ b/src/icons/mono/browser/alipay.svg @@ -0,0 +1 @@ +Alipay \ No newline at end of file diff --git a/src/icons/mono/browser/android-browser.svg b/src/icons/mono/browser/android-browser.svg new file mode 100644 index 000000000..2482150a6 --- /dev/null +++ b/src/icons/mono/browser/android-browser.svg @@ -0,0 +1 @@ +Android Browser \ No newline at end of file diff --git a/src/icons/mono/browser/avast-secure-browser.svg b/src/icons/mono/browser/avast-secure-browser.svg new file mode 100644 index 000000000..b77658a1a --- /dev/null +++ b/src/icons/mono/browser/avast-secure-browser.svg @@ -0,0 +1 @@ +Avast Secure Browser \ No newline at end of file diff --git a/src/icons/mono/browser/baidu-browser.svg b/src/icons/mono/browser/baidu-browser.svg new file mode 100644 index 000000000..f7f0dd163 --- /dev/null +++ b/src/icons/mono/browser/baidu-browser.svg @@ -0,0 +1 @@ +Baidu Browser \ No newline at end of file diff --git a/src/icons/mono/browser/brave.svg b/src/icons/mono/browser/brave.svg new file mode 100644 index 000000000..fa798a452 --- /dev/null +++ b/src/icons/mono/browser/brave.svg @@ -0,0 +1 @@ +Brave \ No newline at end of file diff --git a/src/icons/mono/browser/chrome-headless.svg b/src/icons/mono/browser/chrome-headless.svg new file mode 100644 index 000000000..5fc57d212 --- /dev/null +++ b/src/icons/mono/browser/chrome-headless.svg @@ -0,0 +1 @@ +Chrome Headless \ No newline at end of file diff --git a/src/icons/mono/browser/chrome-webview.svg b/src/icons/mono/browser/chrome-webview.svg new file mode 100644 index 000000000..38b45e32c --- /dev/null +++ b/src/icons/mono/browser/chrome-webview.svg @@ -0,0 +1 @@ +Chrome Webview \ No newline at end of file diff --git a/src/icons/mono/browser/chrome.svg b/src/icons/mono/browser/chrome.svg new file mode 100644 index 000000000..3404d6e21 --- /dev/null +++ b/src/icons/mono/browser/chrome.svg @@ -0,0 +1 @@ +Chrome \ No newline at end of file diff --git a/src/icons/mono/browser/chromium.svg b/src/icons/mono/browser/chromium.svg new file mode 100644 index 000000000..90605418c --- /dev/null +++ b/src/icons/mono/browser/chromium.svg @@ -0,0 +1 @@ +Chromium \ No newline at end of file diff --git a/src/icons/mono/browser/duckduckgo.svg b/src/icons/mono/browser/duckduckgo.svg new file mode 100644 index 000000000..2f466f49e --- /dev/null +++ b/src/icons/mono/browser/duckduckgo.svg @@ -0,0 +1 @@ +DuckDuckGo \ No newline at end of file diff --git a/src/icons/mono/browser/edge.svg b/src/icons/mono/browser/edge.svg new file mode 100644 index 000000000..958a7ed0b --- /dev/null +++ b/src/icons/mono/browser/edge.svg @@ -0,0 +1 @@ +Edge \ No newline at end of file diff --git a/src/icons/mono/browser/electron.svg b/src/icons/mono/browser/electron.svg new file mode 100644 index 000000000..8e855ab30 --- /dev/null +++ b/src/icons/mono/browser/electron.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/mono/browser/facebook.svg b/src/icons/mono/browser/facebook.svg new file mode 100644 index 000000000..f66767e46 --- /dev/null +++ b/src/icons/mono/browser/facebook.svg @@ -0,0 +1 @@ +Facebook \ No newline at end of file diff --git a/src/icons/mono/browser/firefox-focus.svg b/src/icons/mono/browser/firefox-focus.svg new file mode 100644 index 000000000..009082ea8 --- /dev/null +++ b/src/icons/mono/browser/firefox-focus.svg @@ -0,0 +1 @@ +Firefox Focus \ No newline at end of file diff --git a/src/icons/mono/browser/firefox-reality.svg b/src/icons/mono/browser/firefox-reality.svg new file mode 100644 index 000000000..e02dcdfeb --- /dev/null +++ b/src/icons/mono/browser/firefox-reality.svg @@ -0,0 +1 @@ +Firefox Reality \ No newline at end of file diff --git a/src/icons/mono/browser/firefox.svg b/src/icons/mono/browser/firefox.svg new file mode 100644 index 000000000..baace0bad --- /dev/null +++ b/src/icons/mono/browser/firefox.svg @@ -0,0 +1 @@ +Firefox \ No newline at end of file diff --git a/src/icons/mono/browser/gsa.svg b/src/icons/mono/browser/gsa.svg new file mode 100644 index 000000000..753feb554 --- /dev/null +++ b/src/icons/mono/browser/gsa.svg @@ -0,0 +1 @@ +GSA \ No newline at end of file diff --git a/src/icons/mono/browser/huawei-browser.svg b/src/icons/mono/browser/huawei-browser.svg new file mode 100644 index 000000000..200489d3c --- /dev/null +++ b/src/icons/mono/browser/huawei-browser.svg @@ -0,0 +1 @@ +Huawei Browser \ No newline at end of file diff --git a/src/icons/mono/browser/icecat.svg b/src/icons/mono/browser/icecat.svg new file mode 100644 index 000000000..05b01735f --- /dev/null +++ b/src/icons/mono/browser/icecat.svg @@ -0,0 +1 @@ +IceCat \ No newline at end of file diff --git a/src/icons/mono/browser/ie.svg b/src/icons/mono/browser/ie.svg new file mode 100644 index 000000000..f444f15fc --- /dev/null +++ b/src/icons/mono/browser/ie.svg @@ -0,0 +1 @@ +IE \ No newline at end of file diff --git a/src/icons/mono/browser/instagram.svg b/src/icons/mono/browser/instagram.svg new file mode 100644 index 000000000..c0e86b0bf --- /dev/null +++ b/src/icons/mono/browser/instagram.svg @@ -0,0 +1 @@ +Instagram \ No newline at end of file diff --git a/src/icons/mono/browser/jasmine.svg b/src/icons/mono/browser/jasmine.svg new file mode 100644 index 000000000..bda1b3ed9 --- /dev/null +++ b/src/icons/mono/browser/jasmine.svg @@ -0,0 +1 @@ +Jasmine \ No newline at end of file diff --git a/src/icons/mono/browser/kakaotalk.svg b/src/icons/mono/browser/kakaotalk.svg new file mode 100644 index 000000000..8e9f9b123 --- /dev/null +++ b/src/icons/mono/browser/kakaotalk.svg @@ -0,0 +1 @@ +KakaoTalk \ No newline at end of file diff --git a/src/icons/mono/browser/klarna.svg b/src/icons/mono/browser/klarna.svg new file mode 100644 index 000000000..c12c2cef4 --- /dev/null +++ b/src/icons/mono/browser/klarna.svg @@ -0,0 +1 @@ +Klarna \ No newline at end of file diff --git a/src/icons/mono/browser/line.svg b/src/icons/mono/browser/line.svg new file mode 100644 index 000000000..9e4dc98c2 --- /dev/null +++ b/src/icons/mono/browser/line.svg @@ -0,0 +1 @@ +LINE \ No newline at end of file diff --git a/src/icons/mono/browser/linkedin.svg b/src/icons/mono/browser/linkedin.svg new file mode 100644 index 000000000..caa6e69d1 --- /dev/null +++ b/src/icons/mono/browser/linkedin.svg @@ -0,0 +1 @@ +LinkedIn \ No newline at end of file diff --git a/src/icons/mono/browser/miui-browser.svg b/src/icons/mono/browser/miui-browser.svg new file mode 100644 index 000000000..a86433446 --- /dev/null +++ b/src/icons/mono/browser/miui-browser.svg @@ -0,0 +1 @@ +MIUI Browser \ No newline at end of file diff --git a/src/icons/mono/browser/mobile-chrome.svg b/src/icons/mono/browser/mobile-chrome.svg new file mode 100644 index 000000000..cc6792673 --- /dev/null +++ b/src/icons/mono/browser/mobile-chrome.svg @@ -0,0 +1 @@ +Mobile Chrome \ No newline at end of file diff --git a/src/icons/mono/browser/mobile-firefox.svg b/src/icons/mono/browser/mobile-firefox.svg new file mode 100644 index 000000000..b00ccef29 --- /dev/null +++ b/src/icons/mono/browser/mobile-firefox.svg @@ -0,0 +1 @@ +Mobile Firefox \ No newline at end of file diff --git a/src/icons/mono/browser/mobile-safari.svg b/src/icons/mono/browser/mobile-safari.svg new file mode 100644 index 000000000..ac9eab67b --- /dev/null +++ b/src/icons/mono/browser/mobile-safari.svg @@ -0,0 +1 @@ +Mobile Safari \ No newline at end of file diff --git a/src/icons/mono/browser/mozilla.svg b/src/icons/mono/browser/mozilla.svg new file mode 100644 index 000000000..507eb6695 --- /dev/null +++ b/src/icons/mono/browser/mozilla.svg @@ -0,0 +1 @@ +Mozilla \ No newline at end of file diff --git a/src/icons/mono/browser/naver.svg b/src/icons/mono/browser/naver.svg new file mode 100644 index 000000000..611ad4b64 --- /dev/null +++ b/src/icons/mono/browser/naver.svg @@ -0,0 +1 @@ +Naver \ No newline at end of file diff --git a/src/icons/mono/browser/nokia-browser.svg b/src/icons/mono/browser/nokia-browser.svg new file mode 100644 index 000000000..bf4b85c6b --- /dev/null +++ b/src/icons/mono/browser/nokia-browser.svg @@ -0,0 +1 @@ +Nokia Browser \ No newline at end of file diff --git a/src/icons/mono/browser/oculus-browser.svg b/src/icons/mono/browser/oculus-browser.svg new file mode 100644 index 000000000..a0d272f06 --- /dev/null +++ b/src/icons/mono/browser/oculus-browser.svg @@ -0,0 +1 @@ +Oculus Browser \ No newline at end of file diff --git a/src/icons/mono/browser/opera-coast.svg b/src/icons/mono/browser/opera-coast.svg new file mode 100644 index 000000000..f7c88c193 --- /dev/null +++ b/src/icons/mono/browser/opera-coast.svg @@ -0,0 +1 @@ +Opera Coast \ No newline at end of file diff --git a/src/icons/mono/browser/opera-gx.svg b/src/icons/mono/browser/opera-gx.svg new file mode 100644 index 000000000..b560822cb --- /dev/null +++ b/src/icons/mono/browser/opera-gx.svg @@ -0,0 +1 @@ +Opera GX \ No newline at end of file diff --git a/src/icons/mono/browser/opera-mini.svg b/src/icons/mono/browser/opera-mini.svg new file mode 100644 index 000000000..1cc64857a --- /dev/null +++ b/src/icons/mono/browser/opera-mini.svg @@ -0,0 +1 @@ +Opera Mini \ No newline at end of file diff --git a/src/icons/mono/browser/opera-mobi.svg b/src/icons/mono/browser/opera-mobi.svg new file mode 100644 index 000000000..dfc102673 --- /dev/null +++ b/src/icons/mono/browser/opera-mobi.svg @@ -0,0 +1 @@ +Opera Mobi \ No newline at end of file diff --git a/src/icons/mono/browser/opera-tablet.svg b/src/icons/mono/browser/opera-tablet.svg new file mode 100644 index 000000000..d47eea29c --- /dev/null +++ b/src/icons/mono/browser/opera-tablet.svg @@ -0,0 +1 @@ +Opera Tablet \ No newline at end of file diff --git a/src/icons/mono/browser/opera-touch.svg b/src/icons/mono/browser/opera-touch.svg new file mode 100644 index 000000000..50211d4de --- /dev/null +++ b/src/icons/mono/browser/opera-touch.svg @@ -0,0 +1 @@ +Opera Touch \ No newline at end of file diff --git a/src/icons/mono/browser/opera.svg b/src/icons/mono/browser/opera.svg new file mode 100644 index 000000000..d1194ee24 --- /dev/null +++ b/src/icons/mono/browser/opera.svg @@ -0,0 +1 @@ +Opera \ No newline at end of file diff --git a/src/icons/mono/browser/qqbrowser.svg b/src/icons/mono/browser/qqbrowser.svg new file mode 100644 index 000000000..6a94a49f7 --- /dev/null +++ b/src/icons/mono/browser/qqbrowser.svg @@ -0,0 +1 @@ +QQBrowser \ No newline at end of file diff --git a/src/icons/mono/browser/qqbrowserlite.svg b/src/icons/mono/browser/qqbrowserlite.svg new file mode 100644 index 000000000..d4ccef286 --- /dev/null +++ b/src/icons/mono/browser/qqbrowserlite.svg @@ -0,0 +1 @@ +QQBrowserLite \ No newline at end of file diff --git a/src/icons/mono/browser/safari.svg b/src/icons/mono/browser/safari.svg new file mode 100644 index 000000000..5d1543889 --- /dev/null +++ b/src/icons/mono/browser/safari.svg @@ -0,0 +1 @@ +Safari \ No newline at end of file diff --git a/src/icons/mono/browser/sailfish-browser.svg b/src/icons/mono/browser/sailfish-browser.svg new file mode 100644 index 000000000..0884da1c3 --- /dev/null +++ b/src/icons/mono/browser/sailfish-browser.svg @@ -0,0 +1 @@ +Sailfish Browser \ No newline at end of file diff --git a/src/icons/mono/browser/samsung-internet.svg b/src/icons/mono/browser/samsung-internet.svg new file mode 100644 index 000000000..15e89529f --- /dev/null +++ b/src/icons/mono/browser/samsung-internet.svg @@ -0,0 +1 @@ +Samsung Internet \ No newline at end of file diff --git a/src/icons/mono/browser/smart-lenovo-browser.svg b/src/icons/mono/browser/smart-lenovo-browser.svg new file mode 100644 index 000000000..e9be1500a --- /dev/null +++ b/src/icons/mono/browser/smart-lenovo-browser.svg @@ -0,0 +1 @@ +Smart Lenovo Browser \ No newline at end of file diff --git a/src/icons/mono/browser/snapchat.svg b/src/icons/mono/browser/snapchat.svg new file mode 100644 index 000000000..3110fbada --- /dev/null +++ b/src/icons/mono/browser/snapchat.svg @@ -0,0 +1 @@ +Snapchat \ No newline at end of file diff --git a/src/icons/mono/browser/sogou-explorer.svg b/src/icons/mono/browser/sogou-explorer.svg new file mode 100644 index 000000000..1449ecb61 --- /dev/null +++ b/src/icons/mono/browser/sogou-explorer.svg @@ -0,0 +1 @@ +Sogou Explorer \ No newline at end of file diff --git a/src/icons/mono/browser/sogou-mobile.svg b/src/icons/mono/browser/sogou-mobile.svg new file mode 100644 index 000000000..36165fe4c --- /dev/null +++ b/src/icons/mono/browser/sogou-mobile.svg @@ -0,0 +1 @@ +Sogou Mobile \ No newline at end of file diff --git a/src/icons/mono/browser/tesla.svg b/src/icons/mono/browser/tesla.svg new file mode 100644 index 000000000..77e66012d --- /dev/null +++ b/src/icons/mono/browser/tesla.svg @@ -0,0 +1 @@ +Tesla \ No newline at end of file diff --git a/src/icons/mono/browser/tiktok.svg b/src/icons/mono/browser/tiktok.svg new file mode 100644 index 000000000..57ce3ae5d --- /dev/null +++ b/src/icons/mono/browser/tiktok.svg @@ -0,0 +1 @@ +TikTok \ No newline at end of file diff --git a/src/icons/mono/browser/twitter.svg b/src/icons/mono/browser/twitter.svg new file mode 100644 index 000000000..b4aeefd37 --- /dev/null +++ b/src/icons/mono/browser/twitter.svg @@ -0,0 +1 @@ +Twitter \ No newline at end of file diff --git a/src/icons/mono/browser/vivaldi.svg b/src/icons/mono/browser/vivaldi.svg new file mode 100644 index 000000000..167e8ad69 --- /dev/null +++ b/src/icons/mono/browser/vivaldi.svg @@ -0,0 +1 @@ +Vivaldi \ No newline at end of file diff --git a/src/icons/mono/browser/vivo-browser.svg b/src/icons/mono/browser/vivo-browser.svg new file mode 100644 index 000000000..db5270682 --- /dev/null +++ b/src/icons/mono/browser/vivo-browser.svg @@ -0,0 +1 @@ +vivo Browser \ No newline at end of file diff --git a/src/icons/mono/browser/wechat.svg b/src/icons/mono/browser/wechat.svg new file mode 100644 index 000000000..c3eb6c4a6 --- /dev/null +++ b/src/icons/mono/browser/wechat.svg @@ -0,0 +1 @@ +WeChat \ No newline at end of file diff --git a/src/icons/mono/browser/weibo.svg b/src/icons/mono/browser/weibo.svg new file mode 100644 index 000000000..13944085e --- /dev/null +++ b/src/icons/mono/browser/weibo.svg @@ -0,0 +1 @@ +Weibo \ No newline at end of file diff --git a/src/icons/mono/browser/yandex.svg b/src/icons/mono/browser/yandex.svg new file mode 100644 index 000000000..16a76fe16 --- /dev/null +++ b/src/icons/mono/browser/yandex.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/mono/device/acer.svg b/src/icons/mono/device/acer.svg new file mode 100644 index 000000000..ca9b2bbf7 --- /dev/null +++ b/src/icons/mono/device/acer.svg @@ -0,0 +1 @@ +Acer \ No newline at end of file diff --git a/src/icons/mono/device/amazon.svg b/src/icons/mono/device/amazon.svg new file mode 100644 index 000000000..70316babd --- /dev/null +++ b/src/icons/mono/device/amazon.svg @@ -0,0 +1 @@ +Amazon \ No newline at end of file diff --git a/src/icons/mono/device/apple.svg b/src/icons/mono/device/apple.svg new file mode 100644 index 000000000..de4b0d17b --- /dev/null +++ b/src/icons/mono/device/apple.svg @@ -0,0 +1 @@ +Apple \ No newline at end of file diff --git a/src/icons/mono/device/asus.svg b/src/icons/mono/device/asus.svg new file mode 100644 index 000000000..18ef199da --- /dev/null +++ b/src/icons/mono/device/asus.svg @@ -0,0 +1 @@ +ASUS \ No newline at end of file diff --git a/src/icons/mono/device/att.svg b/src/icons/mono/device/att.svg new file mode 100644 index 000000000..166ca3920 --- /dev/null +++ b/src/icons/mono/device/att.svg @@ -0,0 +1 @@ +AT&T \ No newline at end of file diff --git a/src/icons/mono/device/blackberry.svg b/src/icons/mono/device/blackberry.svg new file mode 100644 index 000000000..b0c55837a --- /dev/null +++ b/src/icons/mono/device/blackberry.svg @@ -0,0 +1 @@ +Blackberry \ No newline at end of file diff --git a/src/icons/mono/device/dell.svg b/src/icons/mono/device/dell.svg new file mode 100644 index 000000000..d272bbf6f --- /dev/null +++ b/src/icons/mono/device/dell.svg @@ -0,0 +1 @@ +Dell \ No newline at end of file diff --git a/src/icons/mono/device/facebook.svg b/src/icons/mono/device/facebook.svg new file mode 100644 index 000000000..f66767e46 --- /dev/null +++ b/src/icons/mono/device/facebook.svg @@ -0,0 +1 @@ +Facebook \ No newline at end of file diff --git a/src/icons/mono/device/fairphone.svg b/src/icons/mono/device/fairphone.svg new file mode 100644 index 000000000..3262e30dc --- /dev/null +++ b/src/icons/mono/device/fairphone.svg @@ -0,0 +1 @@ +Fairphone \ No newline at end of file diff --git a/src/icons/mono/device/google.svg b/src/icons/mono/device/google.svg new file mode 100644 index 000000000..2eaf91554 --- /dev/null +++ b/src/icons/mono/device/google.svg @@ -0,0 +1 @@ +Google \ No newline at end of file diff --git a/src/icons/mono/device/hp.svg b/src/icons/mono/device/hp.svg new file mode 100644 index 000000000..04b5f0ef8 --- /dev/null +++ b/src/icons/mono/device/hp.svg @@ -0,0 +1 @@ +HP \ No newline at end of file diff --git a/src/icons/mono/device/huawei.svg b/src/icons/mono/device/huawei.svg new file mode 100644 index 000000000..0c72bab82 --- /dev/null +++ b/src/icons/mono/device/huawei.svg @@ -0,0 +1 @@ +Huawei \ No newline at end of file diff --git a/src/icons/mono/device/lenovo.svg b/src/icons/mono/device/lenovo.svg new file mode 100644 index 000000000..c03719a43 --- /dev/null +++ b/src/icons/mono/device/lenovo.svg @@ -0,0 +1 @@ +Lenovo \ No newline at end of file diff --git a/src/icons/mono/device/lg.svg b/src/icons/mono/device/lg.svg new file mode 100644 index 000000000..5eedc7978 --- /dev/null +++ b/src/icons/mono/device/lg.svg @@ -0,0 +1 @@ +LG \ No newline at end of file diff --git a/src/icons/mono/device/meizu.svg b/src/icons/mono/device/meizu.svg new file mode 100644 index 000000000..8f903f6dc --- /dev/null +++ b/src/icons/mono/device/meizu.svg @@ -0,0 +1 @@ +Meizu \ No newline at end of file diff --git a/src/icons/mono/device/microsoft.svg b/src/icons/mono/device/microsoft.svg new file mode 100644 index 000000000..eeacf2520 --- /dev/null +++ b/src/icons/mono/device/microsoft.svg @@ -0,0 +1 @@ +Microsoft \ No newline at end of file diff --git a/src/icons/mono/device/motorola.svg b/src/icons/mono/device/motorola.svg new file mode 100644 index 000000000..3e744bda2 --- /dev/null +++ b/src/icons/mono/device/motorola.svg @@ -0,0 +1 @@ +Motorola \ No newline at end of file diff --git a/src/icons/mono/device/nintendo.svg b/src/icons/mono/device/nintendo.svg new file mode 100644 index 000000000..5cad9642f --- /dev/null +++ b/src/icons/mono/device/nintendo.svg @@ -0,0 +1 @@ +Nintendo \ No newline at end of file diff --git a/src/icons/mono/device/nokia.svg b/src/icons/mono/device/nokia.svg new file mode 100644 index 000000000..a4a21137e --- /dev/null +++ b/src/icons/mono/device/nokia.svg @@ -0,0 +1 @@ +Nokia \ No newline at end of file diff --git a/src/icons/mono/device/nvidia.svg b/src/icons/mono/device/nvidia.svg new file mode 100644 index 000000000..2c7ff66f8 --- /dev/null +++ b/src/icons/mono/device/nvidia.svg @@ -0,0 +1 @@ +NVIDIA \ No newline at end of file diff --git a/src/icons/mono/device/oneplus.svg b/src/icons/mono/device/oneplus.svg new file mode 100644 index 000000000..11c3131ad --- /dev/null +++ b/src/icons/mono/device/oneplus.svg @@ -0,0 +1 @@ +OnePlus \ No newline at end of file diff --git a/src/icons/mono/device/oppo.svg b/src/icons/mono/device/oppo.svg new file mode 100644 index 000000000..b886af3e7 --- /dev/null +++ b/src/icons/mono/device/oppo.svg @@ -0,0 +1 @@ +OPPO \ No newline at end of file diff --git a/src/icons/mono/device/panasonic.svg b/src/icons/mono/device/panasonic.svg new file mode 100644 index 000000000..e5902436c --- /dev/null +++ b/src/icons/mono/device/panasonic.svg @@ -0,0 +1 @@ +Panasonic \ No newline at end of file diff --git a/src/icons/mono/device/roku.svg b/src/icons/mono/device/roku.svg new file mode 100644 index 000000000..17dde5e36 --- /dev/null +++ b/src/icons/mono/device/roku.svg @@ -0,0 +1 @@ +Roku \ No newline at end of file diff --git a/src/icons/mono/device/samsung.svg b/src/icons/mono/device/samsung.svg new file mode 100644 index 000000000..627cbf2c1 --- /dev/null +++ b/src/icons/mono/device/samsung.svg @@ -0,0 +1 @@ +Samsung \ No newline at end of file diff --git a/src/icons/mono/device/siemens.svg b/src/icons/mono/device/siemens.svg new file mode 100644 index 000000000..ec60e7b17 --- /dev/null +++ b/src/icons/mono/device/siemens.svg @@ -0,0 +1 @@ +Siemens \ No newline at end of file diff --git a/src/icons/mono/device/sony.svg b/src/icons/mono/device/sony.svg new file mode 100644 index 000000000..38d17e13d --- /dev/null +++ b/src/icons/mono/device/sony.svg @@ -0,0 +1 @@ +Sony \ No newline at end of file diff --git a/src/icons/mono/device/tesla.svg b/src/icons/mono/device/tesla.svg new file mode 100644 index 000000000..77e66012d --- /dev/null +++ b/src/icons/mono/device/tesla.svg @@ -0,0 +1 @@ +Tesla \ No newline at end of file diff --git a/src/icons/mono/device/vivo.svg b/src/icons/mono/device/vivo.svg new file mode 100644 index 000000000..0591ac425 --- /dev/null +++ b/src/icons/mono/device/vivo.svg @@ -0,0 +1 @@ +vivo \ No newline at end of file diff --git a/src/icons/mono/device/vodafone.svg b/src/icons/mono/device/vodafone.svg new file mode 100644 index 000000000..f882e2fe0 --- /dev/null +++ b/src/icons/mono/device/vodafone.svg @@ -0,0 +1 @@ +Vodafone \ No newline at end of file diff --git a/src/icons/mono/device/xiaomi.svg b/src/icons/mono/device/xiaomi.svg new file mode 100644 index 000000000..f08eae5bd --- /dev/null +++ b/src/icons/mono/device/xiaomi.svg @@ -0,0 +1 @@ +Xiaomi \ No newline at end of file diff --git a/src/icons/mono/device/zebra.svg b/src/icons/mono/device/zebra.svg new file mode 100644 index 000000000..8282e7744 --- /dev/null +++ b/src/icons/mono/device/zebra.svg @@ -0,0 +1 @@ +Zebra \ No newline at end of file diff --git a/src/icons/mono/os/android.svg b/src/icons/mono/os/android.svg new file mode 100644 index 000000000..3f44ef6ba --- /dev/null +++ b/src/icons/mono/os/android.svg @@ -0,0 +1 @@ +Android \ No newline at end of file diff --git a/src/icons/mono/os/arch.svg b/src/icons/mono/os/arch.svg new file mode 100644 index 000000000..a331ef91d --- /dev/null +++ b/src/icons/mono/os/arch.svg @@ -0,0 +1 @@ +Arch \ No newline at end of file diff --git a/src/icons/mono/os/blackberry.svg b/src/icons/mono/os/blackberry.svg new file mode 100644 index 000000000..b0c55837a --- /dev/null +++ b/src/icons/mono/os/blackberry.svg @@ -0,0 +1 @@ +Blackberry \ No newline at end of file diff --git a/src/icons/mono/os/centos.svg b/src/icons/mono/os/centos.svg new file mode 100644 index 000000000..3f16a9e00 --- /dev/null +++ b/src/icons/mono/os/centos.svg @@ -0,0 +1 @@ +CentOS \ No newline at end of file diff --git a/src/icons/mono/os/chrome_os.svg b/src/icons/mono/os/chrome_os.svg new file mode 100644 index 000000000..0a282625e --- /dev/null +++ b/src/icons/mono/os/chrome_os.svg @@ -0,0 +1 @@ +Chrome OS \ No newline at end of file diff --git a/src/icons/mono/os/chromecast.svg b/src/icons/mono/os/chromecast.svg new file mode 100644 index 000000000..6b251ed8a --- /dev/null +++ b/src/icons/mono/os/chromecast.svg @@ -0,0 +1 @@ +Chromecast \ No newline at end of file diff --git a/src/icons/mono/os/debian.svg b/src/icons/mono/os/debian.svg new file mode 100644 index 000000000..c983b3245 --- /dev/null +++ b/src/icons/mono/os/debian.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/mono/os/deepin.svg b/src/icons/mono/os/deepin.svg new file mode 100644 index 000000000..2a5fbca2d --- /dev/null +++ b/src/icons/mono/os/deepin.svg @@ -0,0 +1 @@ +deepin \ No newline at end of file diff --git a/src/icons/mono/os/elementary_os.svg b/src/icons/mono/os/elementary_os.svg new file mode 100644 index 000000000..2b4c9515b --- /dev/null +++ b/src/icons/mono/os/elementary_os.svg @@ -0,0 +1 @@ +elementary OS \ No newline at end of file diff --git a/src/icons/mono/os/fedora.svg b/src/icons/mono/os/fedora.svg new file mode 100644 index 000000000..7f461d0e6 --- /dev/null +++ b/src/icons/mono/os/fedora.svg @@ -0,0 +1 @@ +Fedora \ No newline at end of file diff --git a/src/icons/mono/os/firefox.svg b/src/icons/mono/os/firefox.svg new file mode 100644 index 000000000..e7c9fff23 --- /dev/null +++ b/src/icons/mono/os/firefox.svg @@ -0,0 +1 @@ +Firefox \ No newline at end of file diff --git a/src/icons/mono/os/freebsd.svg b/src/icons/mono/os/freebsd.svg new file mode 100644 index 000000000..8febe9d4c --- /dev/null +++ b/src/icons/mono/os/freebsd.svg @@ -0,0 +1 @@ +FreeBSD \ No newline at end of file diff --git a/src/icons/mono/os/gentoo.svg b/src/icons/mono/os/gentoo.svg new file mode 100644 index 000000000..4c48a0227 --- /dev/null +++ b/src/icons/mono/os/gentoo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/mono/os/gnu.svg b/src/icons/mono/os/gnu.svg new file mode 100644 index 000000000..f29f4d7e4 --- /dev/null +++ b/src/icons/mono/os/gnu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/mono/os/harmonyos.svg b/src/icons/mono/os/harmonyos.svg new file mode 100644 index 000000000..99b8d7e62 --- /dev/null +++ b/src/icons/mono/os/harmonyos.svg @@ -0,0 +1 @@ +HarmonyOS \ No newline at end of file diff --git a/src/icons/mono/os/ios.svg b/src/icons/mono/os/ios.svg new file mode 100644 index 000000000..a16369768 --- /dev/null +++ b/src/icons/mono/os/ios.svg @@ -0,0 +1 @@ +iOS \ No newline at end of file diff --git a/src/icons/mono/os/kaios.svg b/src/icons/mono/os/kaios.svg new file mode 100644 index 000000000..27a0ec770 --- /dev/null +++ b/src/icons/mono/os/kaios.svg @@ -0,0 +1 @@ +KaiOS \ No newline at end of file diff --git a/src/icons/mono/os/linux.svg b/src/icons/mono/os/linux.svg new file mode 100644 index 000000000..381d3d8a8 --- /dev/null +++ b/src/icons/mono/os/linux.svg @@ -0,0 +1 @@ +Linux \ No newline at end of file diff --git a/src/icons/mono/os/macos.svg b/src/icons/mono/os/macos.svg new file mode 100644 index 000000000..87630c456 --- /dev/null +++ b/src/icons/mono/os/macos.svg @@ -0,0 +1 @@ +macOS \ No newline at end of file diff --git a/src/icons/mono/os/manjaro.svg b/src/icons/mono/os/manjaro.svg new file mode 100644 index 000000000..d99972e2a --- /dev/null +++ b/src/icons/mono/os/manjaro.svg @@ -0,0 +1 @@ +Manjaro \ No newline at end of file diff --git a/src/icons/mono/os/mint.svg b/src/icons/mono/os/mint.svg new file mode 100644 index 000000000..442c8b2d5 --- /dev/null +++ b/src/icons/mono/os/mint.svg @@ -0,0 +1 @@ +Mint \ No newline at end of file diff --git a/src/icons/mono/os/netbsd.svg b/src/icons/mono/os/netbsd.svg new file mode 100644 index 000000000..511f066ed --- /dev/null +++ b/src/icons/mono/os/netbsd.svg @@ -0,0 +1 @@ +NetBSD \ No newline at end of file diff --git a/src/icons/mono/os/nintendo.svg b/src/icons/mono/os/nintendo.svg new file mode 100644 index 000000000..5cad9642f --- /dev/null +++ b/src/icons/mono/os/nintendo.svg @@ -0,0 +1 @@ +Nintendo \ No newline at end of file diff --git a/src/icons/mono/os/openbsd.svg b/src/icons/mono/os/openbsd.svg new file mode 100644 index 000000000..e78fbc7e6 --- /dev/null +++ b/src/icons/mono/os/openbsd.svg @@ -0,0 +1 @@ +OpenBSD \ No newline at end of file diff --git a/src/icons/mono/os/playstation.svg b/src/icons/mono/os/playstation.svg new file mode 100644 index 000000000..be3370316 --- /dev/null +++ b/src/icons/mono/os/playstation.svg @@ -0,0 +1 @@ +PlayStation \ No newline at end of file diff --git a/src/icons/mono/os/raspbian.svg b/src/icons/mono/os/raspbian.svg new file mode 100644 index 000000000..de6feed52 --- /dev/null +++ b/src/icons/mono/os/raspbian.svg @@ -0,0 +1 @@ +Raspbian \ No newline at end of file diff --git a/src/icons/mono/os/redhat.svg b/src/icons/mono/os/redhat.svg new file mode 100644 index 000000000..b68e41333 --- /dev/null +++ b/src/icons/mono/os/redhat.svg @@ -0,0 +1 @@ +RedHat \ No newline at end of file diff --git a/src/icons/mono/os/sailfish.svg b/src/icons/mono/os/sailfish.svg new file mode 100644 index 000000000..b695e10b4 --- /dev/null +++ b/src/icons/mono/os/sailfish.svg @@ -0,0 +1 @@ +Sailfish \ No newline at end of file diff --git a/src/icons/mono/os/slackware.svg b/src/icons/mono/os/slackware.svg new file mode 100644 index 000000000..611bf809d --- /dev/null +++ b/src/icons/mono/os/slackware.svg @@ -0,0 +1 @@ +Slackware \ No newline at end of file diff --git a/src/icons/mono/os/suse.svg b/src/icons/mono/os/suse.svg new file mode 100644 index 000000000..a4e474a1d --- /dev/null +++ b/src/icons/mono/os/suse.svg @@ -0,0 +1 @@ +SUSE \ No newline at end of file diff --git a/src/icons/mono/os/ubuntu.svg b/src/icons/mono/os/ubuntu.svg new file mode 100644 index 000000000..227f86aa3 --- /dev/null +++ b/src/icons/mono/os/ubuntu.svg @@ -0,0 +1 @@ +Ubuntu \ No newline at end of file diff --git a/src/icons/mono/os/windows.svg b/src/icons/mono/os/windows.svg new file mode 100644 index 000000000..9c79dffdb --- /dev/null +++ b/src/icons/mono/os/windows.svg @@ -0,0 +1 @@ +Windows 10 \ No newline at end of file diff --git a/src/icons/mono/os/xbox.svg b/src/icons/mono/os/xbox.svg new file mode 100644 index 000000000..85a9689a6 --- /dev/null +++ b/src/icons/mono/os/xbox.svg @@ -0,0 +1 @@ +Xbox \ No newline at end of file diff --git a/src/icons/ua-parser-icons.js b/src/icons/ua-parser-icons.js new file mode 100644 index 000000000..e69de29bb From fe15f115f9c5b811e72836cb68a8497004976d64 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 22 Oct 2024 21:36:46 +0700 Subject: [PATCH 238/388] Fix #754: Add new Engine: ArkWeb & new OS: OpenHarmony --- src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 7 +++++-- test/specs/engine-all.json | 9 +++++++++ test/specs/os-all.json | 9 +++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 9f58b6158..e80a0d57a 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -255,6 +255,7 @@ const Vendor = Object.freeze({ const Engine = Object.freeze({ AMAYA: 'Amaya', + ARKWEB: 'ArkWeb', BLINK: 'Blink', EDGEHTML: 'EdgeHTML', FLOW: 'Flow', @@ -321,6 +322,7 @@ const OS = Object.freeze({ NETRANGE: 'NetRange', NETTV: 'NetTV', NINTENDO: 'Nintendo', + OPENHARMONY: 'OpenHarmony', OPENBSD: 'OpenBSD', OPENVMS: 'OpenVMS', OS2: 'OS/2', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 59ac6553d..0eb24efd1 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -794,6 +794,9 @@ /windows.+ edge\/([\w\.]+)/i // EdgeHTML ], [VERSION, [NAME, EDGE+'HTML']], [ + /(arkweb)\/([\w\.]+)/i // ArkWeb + ], [NAME, VERSION], [ + /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink ], [VERSION, [NAME, 'Blink']], [ @@ -844,8 +847,8 @@ // Mobile OSes /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS - ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS - /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i, + ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS/OpenHarmony + /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish|openharmony)[-\/ ]?([\w\.]*)/i, /(blackberry)\w*\/([\w\.]*)/i, // Blackberry /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS /\((series40);/i // Series 40 diff --git a/test/specs/engine-all.json b/test/specs/engine-all.json index e75e539cc..67fdd3381 100644 --- a/test/specs/engine-all.json +++ b/test/specs/engine-all.json @@ -1,4 +1,13 @@ [ + { + "desc" : "ArkWeb", + "ua" : "Mozilla/5.0 (Phone; OpenHarmony 4.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile", + "expect" : + { + "name" : "ArkWeb", + "version" : "4.1.6.1" + } + }, { "desc" : "Blink", "ua" : "Mozilla/5.0 (Linux; Android 7.0; SM-G920I Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/3.4.9 SamsungBrowser/4.0 Chrome/57.0.2987.146 Mobile VR Safari/537.36", diff --git a/test/specs/os-all.json b/test/specs/os-all.json index 6fc03ecdd..b93dc33bd 100644 --- a/test/specs/os-all.json +++ b/test/specs/os-all.json @@ -1309,5 +1309,14 @@ "name" : "SerenityOS", "version" : "undefined" } + }, + { + "desc" : "OpenHarmony", + "ua" : "Mozilla/5.0 (Phone; OpenHarmony 4.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile", + "expect" : + { + "name" : "OpenHarmony", + "version" : "4.1" + } } ] From 9890f9f5f80e7ffeec7392175c2a8fccb81df8a9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 25 Oct 2024 10:31:07 +0700 Subject: [PATCH 239/388] Move icons under `/dist` folder --- dist/icons/color/LICENSE.md | 3 +++ dist/icons/color/browser/android-browser.svg | 1 + dist/icons/color/browser/avant.png | Bin 0 -> 21415 bytes dist/icons/color/browser/basilisk.png | Bin 0 -> 6366 bytes dist/icons/color/browser/basilisk.svg | 1 + dist/icons/color/browser/brave.png | Bin 0 -> 7114 bytes dist/icons/color/browser/brave.svg | 1 + dist/icons/color/browser/chrome.png | Bin 0 -> 8883 bytes dist/icons/color/browser/chrome.svg | 1 + dist/icons/color/browser/chromium.png | Bin 0 -> 5190 bytes dist/icons/color/browser/chromium.svg | 1 + dist/icons/color/browser/coc-coc.png | Bin 0 -> 18330 bytes dist/icons/color/browser/default.png | Bin 0 -> 6914 bytes dist/icons/color/browser/default.svg | 1 + dist/icons/color/browser/dolphin.png | Bin 0 -> 5689 bytes dist/icons/color/browser/edge.png | Bin 0 -> 11311 bytes dist/icons/color/browser/edge.svg | 1 + dist/icons/color/browser/electron.png | Bin 0 -> 4667 bytes dist/icons/color/browser/electron.svg | 1 + dist/icons/color/browser/falkon.png | Bin 0 -> 13611 bytes dist/icons/color/browser/falkon.svg | 1 + dist/icons/color/browser/firefox-reality.png | Bin 0 -> 16015 bytes dist/icons/color/browser/firefox-reality.svg | 1 + dist/icons/color/browser/firefox.png | Bin 0 -> 15035 bytes dist/icons/color/browser/firefox.svg | 1 + dist/icons/color/browser/gsa.svg | 9 +++++++++ dist/icons/color/browser/icecat.png | Bin 0 -> 24923 bytes dist/icons/color/browser/ie.svg | 6 ++++++ dist/icons/color/browser/konqueror.png | Bin 0 -> 15036 bytes dist/icons/color/browser/konqueror.svg | 1 + dist/icons/color/browser/maxthon.png | Bin 0 -> 4754 bytes dist/icons/color/browser/midori.png | Bin 0 -> 14611 bytes dist/icons/color/browser/midori.svg | 1 + dist/icons/color/browser/netsurf.png | Bin 0 -> 12702 bytes dist/icons/color/browser/netsurf.svg | 1 + dist/icons/color/browser/opera-gx.png | Bin 0 -> 4922 bytes dist/icons/color/browser/opera-gx.svg | 1 + dist/icons/color/browser/opera-mini.png | Bin 0 -> 9242 bytes dist/icons/color/browser/opera-touch.png | Bin 0 -> 8351 bytes dist/icons/color/browser/opera.png | Bin 0 -> 5749 bytes dist/icons/color/browser/opera.svg | 1 + dist/icons/color/browser/palemoon.png | Bin 0 -> 23124 bytes dist/icons/color/browser/puffin.png | Bin 0 -> 15274 bytes dist/icons/color/browser/safari.png | Bin 0 -> 21132 bytes dist/icons/color/browser/safari.svg | 1 + dist/icons/color/browser/samsung-internet.png | Bin 0 -> 5192 bytes dist/icons/color/browser/samsung-internet.svg | 1 + dist/icons/color/browser/seamonkey.png | Bin 0 -> 16454 bytes dist/icons/color/browser/silk.png | Bin 0 -> 9184 bytes dist/icons/color/browser/sogou-mobile.png | Bin 0 -> 7704 bytes dist/icons/color/browser/ucbrowser.png | Bin 0 -> 9026 bytes dist/icons/color/browser/ucbrowser.svg | 1 + dist/icons/color/browser/vivaldi.png | Bin 0 -> 7634 bytes dist/icons/color/browser/vivaldi.svg | 1 + dist/icons/color/browser/webkit.png | Bin 0 -> 11500 bytes dist/icons/color/browser/webkit.svg | 1 + dist/icons/color/browser/yandex.png | Bin 0 -> 10997 bytes dist/icons/color/os/centos.svg | 1 + {src => dist}/icons/mono/LICENSE.md | 0 {src => dist}/icons/mono/browser/alipay.svg | 0 .../icons/mono/browser/android-browser.svg | 0 .../icons/mono/browser/avast-secure-browser.svg | 0 .../icons/mono/browser/baidu-browser.svg | 0 {src => dist}/icons/mono/browser/brave.svg | 0 .../icons/mono/browser/chrome-headless.svg | 0 .../icons/mono/browser/chrome-webview.svg | 0 {src => dist}/icons/mono/browser/chrome.svg | 0 {src => dist}/icons/mono/browser/chromium.svg | 0 {src => dist}/icons/mono/browser/duckduckgo.svg | 0 {src => dist}/icons/mono/browser/edge.svg | 0 {src => dist}/icons/mono/browser/electron.svg | 0 {src => dist}/icons/mono/browser/facebook.svg | 0 .../icons/mono/browser/firefox-focus.svg | 0 .../icons/mono/browser/firefox-reality.svg | 0 {src => dist}/icons/mono/browser/firefox.svg | 0 {src => dist}/icons/mono/browser/gsa.svg | 0 .../icons/mono/browser/huawei-browser.svg | 0 {src => dist}/icons/mono/browser/icecat.svg | 0 {src => dist}/icons/mono/browser/ie.svg | 0 {src => dist}/icons/mono/browser/instagram.svg | 0 {src => dist}/icons/mono/browser/jasmine.svg | 0 {src => dist}/icons/mono/browser/kakaotalk.svg | 0 {src => dist}/icons/mono/browser/klarna.svg | 0 {src => dist}/icons/mono/browser/line.svg | 0 {src => dist}/icons/mono/browser/linkedin.svg | 0 .../icons/mono/browser/miui-browser.svg | 0 .../icons/mono/browser/mobile-chrome.svg | 0 .../icons/mono/browser/mobile-firefox.svg | 0 .../icons/mono/browser/mobile-safari.svg | 0 {src => dist}/icons/mono/browser/mozilla.svg | 0 {src => dist}/icons/mono/browser/naver.svg | 0 .../icons/mono/browser/nokia-browser.svg | 0 .../icons/mono/browser/oculus-browser.svg | 0 .../icons/mono/browser/opera-coast.svg | 0 {src => dist}/icons/mono/browser/opera-gx.svg | 0 {src => dist}/icons/mono/browser/opera-mini.svg | 0 {src => dist}/icons/mono/browser/opera-mobi.svg | 0 .../icons/mono/browser/opera-tablet.svg | 0 .../icons/mono/browser/opera-touch.svg | 0 {src => dist}/icons/mono/browser/opera.svg | 0 {src => dist}/icons/mono/browser/qqbrowser.svg | 0 .../icons/mono/browser/qqbrowserlite.svg | 0 {src => dist}/icons/mono/browser/safari.svg | 0 .../icons/mono/browser/sailfish-browser.svg | 0 .../icons/mono/browser/samsung-internet.svg | 0 .../icons/mono/browser/smart-lenovo-browser.svg | 0 {src => dist}/icons/mono/browser/snapchat.svg | 0 .../icons/mono/browser/sogou-explorer.svg | 0 .../icons/mono/browser/sogou-mobile.svg | 0 {src => dist}/icons/mono/browser/tesla.svg | 0 {src => dist}/icons/mono/browser/tiktok.svg | 0 {src => dist}/icons/mono/browser/twitter.svg | 0 {src => dist}/icons/mono/browser/vivaldi.svg | 0 .../icons/mono/browser/vivo-browser.svg | 0 {src => dist}/icons/mono/browser/wechat.svg | 0 {src => dist}/icons/mono/browser/weibo.svg | 0 {src => dist}/icons/mono/browser/yandex.svg | 0 {src => dist}/icons/mono/device/acer.svg | 0 {src => dist}/icons/mono/device/amazon.svg | 0 {src => dist}/icons/mono/device/apple.svg | 0 {src => dist}/icons/mono/device/asus.svg | 0 {src => dist}/icons/mono/device/att.svg | 0 {src => dist}/icons/mono/device/blackberry.svg | 0 {src => dist}/icons/mono/device/dell.svg | 0 {src => dist}/icons/mono/device/facebook.svg | 0 {src => dist}/icons/mono/device/fairphone.svg | 0 {src => dist}/icons/mono/device/google.svg | 0 {src => dist}/icons/mono/device/hp.svg | 0 {src => dist}/icons/mono/device/huawei.svg | 0 {src => dist}/icons/mono/device/lenovo.svg | 0 {src => dist}/icons/mono/device/lg.svg | 0 {src => dist}/icons/mono/device/meizu.svg | 0 {src => dist}/icons/mono/device/microsoft.svg | 0 {src => dist}/icons/mono/device/motorola.svg | 0 {src => dist}/icons/mono/device/nintendo.svg | 0 {src => dist}/icons/mono/device/nokia.svg | 0 {src => dist}/icons/mono/device/nvidia.svg | 0 {src => dist}/icons/mono/device/oneplus.svg | 0 {src => dist}/icons/mono/device/oppo.svg | 0 {src => dist}/icons/mono/device/panasonic.svg | 0 {src => dist}/icons/mono/device/roku.svg | 0 {src => dist}/icons/mono/device/samsung.svg | 0 {src => dist}/icons/mono/device/siemens.svg | 0 {src => dist}/icons/mono/device/sony.svg | 0 {src => dist}/icons/mono/device/tesla.svg | 0 {src => dist}/icons/mono/device/vivo.svg | 0 {src => dist}/icons/mono/device/vodafone.svg | 0 {src => dist}/icons/mono/device/xiaomi.svg | 0 {src => dist}/icons/mono/device/zebra.svg | 0 {src => dist}/icons/mono/os/android.svg | 0 {src => dist}/icons/mono/os/arch.svg | 0 {src => dist}/icons/mono/os/blackberry.svg | 0 {src => dist}/icons/mono/os/centos.svg | 0 .../icons/mono/os/chrome-os.svg | 0 {src => dist}/icons/mono/os/chromecast.svg | 0 {src => dist}/icons/mono/os/debian.svg | 0 {src => dist}/icons/mono/os/deepin.svg | 0 .../icons/mono/os/elementary-os.svg | 0 {src => dist}/icons/mono/os/fedora.svg | 0 {src => dist}/icons/mono/os/firefox.svg | 0 {src => dist}/icons/mono/os/freebsd.svg | 0 {src => dist}/icons/mono/os/gentoo.svg | 0 {src => dist}/icons/mono/os/gnu.svg | 0 {src => dist}/icons/mono/os/harmonyos.svg | 0 {src => dist}/icons/mono/os/ios.svg | 0 {src => dist}/icons/mono/os/kaios.svg | 0 {src => dist}/icons/mono/os/linux.svg | 0 {src => dist}/icons/mono/os/macos.svg | 0 {src => dist}/icons/mono/os/manjaro.svg | 0 {src => dist}/icons/mono/os/mint.svg | 0 {src => dist}/icons/mono/os/netbsd.svg | 0 {src => dist}/icons/mono/os/nintendo.svg | 0 {src => dist}/icons/mono/os/openbsd.svg | 0 {src => dist}/icons/mono/os/playstation.svg | 0 {src => dist}/icons/mono/os/raspbian.svg | 0 {src => dist}/icons/mono/os/redhat.svg | 0 {src => dist}/icons/mono/os/sailfish.svg | 0 {src => dist}/icons/mono/os/slackware.svg | 0 {src => dist}/icons/mono/os/suse.svg | 0 {src => dist}/icons/mono/os/ubuntu.svg | 0 {src => dist}/icons/mono/os/windows.svg | 0 {src => dist}/icons/mono/os/xbox.svg | 0 src/icons/ua-parser-icons.js | 0 183 files changed, 40 insertions(+) create mode 100644 dist/icons/color/LICENSE.md create mode 100644 dist/icons/color/browser/android-browser.svg create mode 100644 dist/icons/color/browser/avant.png create mode 100644 dist/icons/color/browser/basilisk.png create mode 100644 dist/icons/color/browser/basilisk.svg create mode 100644 dist/icons/color/browser/brave.png create mode 100644 dist/icons/color/browser/brave.svg create mode 100644 dist/icons/color/browser/chrome.png create mode 100644 dist/icons/color/browser/chrome.svg create mode 100644 dist/icons/color/browser/chromium.png create mode 100644 dist/icons/color/browser/chromium.svg create mode 100644 dist/icons/color/browser/coc-coc.png create mode 100644 dist/icons/color/browser/default.png create mode 100644 dist/icons/color/browser/default.svg create mode 100644 dist/icons/color/browser/dolphin.png create mode 100644 dist/icons/color/browser/edge.png create mode 100644 dist/icons/color/browser/edge.svg create mode 100644 dist/icons/color/browser/electron.png create mode 100644 dist/icons/color/browser/electron.svg create mode 100644 dist/icons/color/browser/falkon.png create mode 100644 dist/icons/color/browser/falkon.svg create mode 100644 dist/icons/color/browser/firefox-reality.png create mode 100644 dist/icons/color/browser/firefox-reality.svg create mode 100644 dist/icons/color/browser/firefox.png create mode 100644 dist/icons/color/browser/firefox.svg create mode 100644 dist/icons/color/browser/gsa.svg create mode 100644 dist/icons/color/browser/icecat.png create mode 100644 dist/icons/color/browser/ie.svg create mode 100644 dist/icons/color/browser/konqueror.png create mode 100644 dist/icons/color/browser/konqueror.svg create mode 100644 dist/icons/color/browser/maxthon.png create mode 100644 dist/icons/color/browser/midori.png create mode 100644 dist/icons/color/browser/midori.svg create mode 100644 dist/icons/color/browser/netsurf.png create mode 100644 dist/icons/color/browser/netsurf.svg create mode 100644 dist/icons/color/browser/opera-gx.png create mode 100644 dist/icons/color/browser/opera-gx.svg create mode 100644 dist/icons/color/browser/opera-mini.png create mode 100644 dist/icons/color/browser/opera-touch.png create mode 100644 dist/icons/color/browser/opera.png create mode 100644 dist/icons/color/browser/opera.svg create mode 100644 dist/icons/color/browser/palemoon.png create mode 100644 dist/icons/color/browser/puffin.png create mode 100644 dist/icons/color/browser/safari.png create mode 100644 dist/icons/color/browser/safari.svg create mode 100644 dist/icons/color/browser/samsung-internet.png create mode 100644 dist/icons/color/browser/samsung-internet.svg create mode 100644 dist/icons/color/browser/seamonkey.png create mode 100644 dist/icons/color/browser/silk.png create mode 100644 dist/icons/color/browser/sogou-mobile.png create mode 100644 dist/icons/color/browser/ucbrowser.png create mode 100644 dist/icons/color/browser/ucbrowser.svg create mode 100644 dist/icons/color/browser/vivaldi.png create mode 100644 dist/icons/color/browser/vivaldi.svg create mode 100644 dist/icons/color/browser/webkit.png create mode 100644 dist/icons/color/browser/webkit.svg create mode 100644 dist/icons/color/browser/yandex.png create mode 100644 dist/icons/color/os/centos.svg rename {src => dist}/icons/mono/LICENSE.md (100%) rename {src => dist}/icons/mono/browser/alipay.svg (100%) rename {src => dist}/icons/mono/browser/android-browser.svg (100%) rename {src => dist}/icons/mono/browser/avast-secure-browser.svg (100%) rename {src => dist}/icons/mono/browser/baidu-browser.svg (100%) rename {src => dist}/icons/mono/browser/brave.svg (100%) rename {src => dist}/icons/mono/browser/chrome-headless.svg (100%) rename {src => dist}/icons/mono/browser/chrome-webview.svg (100%) rename {src => dist}/icons/mono/browser/chrome.svg (100%) rename {src => dist}/icons/mono/browser/chromium.svg (100%) rename {src => dist}/icons/mono/browser/duckduckgo.svg (100%) rename {src => dist}/icons/mono/browser/edge.svg (100%) rename {src => dist}/icons/mono/browser/electron.svg (100%) rename {src => dist}/icons/mono/browser/facebook.svg (100%) rename {src => dist}/icons/mono/browser/firefox-focus.svg (100%) rename {src => dist}/icons/mono/browser/firefox-reality.svg (100%) rename {src => dist}/icons/mono/browser/firefox.svg (100%) rename {src => dist}/icons/mono/browser/gsa.svg (100%) rename {src => dist}/icons/mono/browser/huawei-browser.svg (100%) rename {src => dist}/icons/mono/browser/icecat.svg (100%) rename {src => dist}/icons/mono/browser/ie.svg (100%) rename {src => dist}/icons/mono/browser/instagram.svg (100%) rename {src => dist}/icons/mono/browser/jasmine.svg (100%) rename {src => dist}/icons/mono/browser/kakaotalk.svg (100%) rename {src => dist}/icons/mono/browser/klarna.svg (100%) rename {src => dist}/icons/mono/browser/line.svg (100%) rename {src => dist}/icons/mono/browser/linkedin.svg (100%) rename {src => dist}/icons/mono/browser/miui-browser.svg (100%) rename {src => dist}/icons/mono/browser/mobile-chrome.svg (100%) rename {src => dist}/icons/mono/browser/mobile-firefox.svg (100%) rename {src => dist}/icons/mono/browser/mobile-safari.svg (100%) rename {src => dist}/icons/mono/browser/mozilla.svg (100%) rename {src => dist}/icons/mono/browser/naver.svg (100%) rename {src => dist}/icons/mono/browser/nokia-browser.svg (100%) rename {src => dist}/icons/mono/browser/oculus-browser.svg (100%) rename {src => dist}/icons/mono/browser/opera-coast.svg (100%) rename {src => dist}/icons/mono/browser/opera-gx.svg (100%) rename {src => dist}/icons/mono/browser/opera-mini.svg (100%) rename {src => dist}/icons/mono/browser/opera-mobi.svg (100%) rename {src => dist}/icons/mono/browser/opera-tablet.svg (100%) rename {src => dist}/icons/mono/browser/opera-touch.svg (100%) rename {src => dist}/icons/mono/browser/opera.svg (100%) rename {src => dist}/icons/mono/browser/qqbrowser.svg (100%) rename {src => dist}/icons/mono/browser/qqbrowserlite.svg (100%) rename {src => dist}/icons/mono/browser/safari.svg (100%) rename {src => dist}/icons/mono/browser/sailfish-browser.svg (100%) rename {src => dist}/icons/mono/browser/samsung-internet.svg (100%) rename {src => dist}/icons/mono/browser/smart-lenovo-browser.svg (100%) rename {src => dist}/icons/mono/browser/snapchat.svg (100%) rename {src => dist}/icons/mono/browser/sogou-explorer.svg (100%) rename {src => dist}/icons/mono/browser/sogou-mobile.svg (100%) rename {src => dist}/icons/mono/browser/tesla.svg (100%) rename {src => dist}/icons/mono/browser/tiktok.svg (100%) rename {src => dist}/icons/mono/browser/twitter.svg (100%) rename {src => dist}/icons/mono/browser/vivaldi.svg (100%) rename {src => dist}/icons/mono/browser/vivo-browser.svg (100%) rename {src => dist}/icons/mono/browser/wechat.svg (100%) rename {src => dist}/icons/mono/browser/weibo.svg (100%) rename {src => dist}/icons/mono/browser/yandex.svg (100%) rename {src => dist}/icons/mono/device/acer.svg (100%) rename {src => dist}/icons/mono/device/amazon.svg (100%) rename {src => dist}/icons/mono/device/apple.svg (100%) rename {src => dist}/icons/mono/device/asus.svg (100%) rename {src => dist}/icons/mono/device/att.svg (100%) rename {src => dist}/icons/mono/device/blackberry.svg (100%) rename {src => dist}/icons/mono/device/dell.svg (100%) rename {src => dist}/icons/mono/device/facebook.svg (100%) rename {src => dist}/icons/mono/device/fairphone.svg (100%) rename {src => dist}/icons/mono/device/google.svg (100%) rename {src => dist}/icons/mono/device/hp.svg (100%) rename {src => dist}/icons/mono/device/huawei.svg (100%) rename {src => dist}/icons/mono/device/lenovo.svg (100%) rename {src => dist}/icons/mono/device/lg.svg (100%) rename {src => dist}/icons/mono/device/meizu.svg (100%) rename {src => dist}/icons/mono/device/microsoft.svg (100%) rename {src => dist}/icons/mono/device/motorola.svg (100%) rename {src => dist}/icons/mono/device/nintendo.svg (100%) rename {src => dist}/icons/mono/device/nokia.svg (100%) rename {src => dist}/icons/mono/device/nvidia.svg (100%) rename {src => dist}/icons/mono/device/oneplus.svg (100%) rename {src => dist}/icons/mono/device/oppo.svg (100%) rename {src => dist}/icons/mono/device/panasonic.svg (100%) rename {src => dist}/icons/mono/device/roku.svg (100%) rename {src => dist}/icons/mono/device/samsung.svg (100%) rename {src => dist}/icons/mono/device/siemens.svg (100%) rename {src => dist}/icons/mono/device/sony.svg (100%) rename {src => dist}/icons/mono/device/tesla.svg (100%) rename {src => dist}/icons/mono/device/vivo.svg (100%) rename {src => dist}/icons/mono/device/vodafone.svg (100%) rename {src => dist}/icons/mono/device/xiaomi.svg (100%) rename {src => dist}/icons/mono/device/zebra.svg (100%) rename {src => dist}/icons/mono/os/android.svg (100%) rename {src => dist}/icons/mono/os/arch.svg (100%) rename {src => dist}/icons/mono/os/blackberry.svg (100%) rename {src => dist}/icons/mono/os/centos.svg (100%) rename src/icons/mono/os/chrome_os.svg => dist/icons/mono/os/chrome-os.svg (100%) rename {src => dist}/icons/mono/os/chromecast.svg (100%) rename {src => dist}/icons/mono/os/debian.svg (100%) rename {src => dist}/icons/mono/os/deepin.svg (100%) rename src/icons/mono/os/elementary_os.svg => dist/icons/mono/os/elementary-os.svg (100%) rename {src => dist}/icons/mono/os/fedora.svg (100%) rename {src => dist}/icons/mono/os/firefox.svg (100%) rename {src => dist}/icons/mono/os/freebsd.svg (100%) rename {src => dist}/icons/mono/os/gentoo.svg (100%) rename {src => dist}/icons/mono/os/gnu.svg (100%) rename {src => dist}/icons/mono/os/harmonyos.svg (100%) rename {src => dist}/icons/mono/os/ios.svg (100%) rename {src => dist}/icons/mono/os/kaios.svg (100%) rename {src => dist}/icons/mono/os/linux.svg (100%) rename {src => dist}/icons/mono/os/macos.svg (100%) rename {src => dist}/icons/mono/os/manjaro.svg (100%) rename {src => dist}/icons/mono/os/mint.svg (100%) rename {src => dist}/icons/mono/os/netbsd.svg (100%) rename {src => dist}/icons/mono/os/nintendo.svg (100%) rename {src => dist}/icons/mono/os/openbsd.svg (100%) rename {src => dist}/icons/mono/os/playstation.svg (100%) rename {src => dist}/icons/mono/os/raspbian.svg (100%) rename {src => dist}/icons/mono/os/redhat.svg (100%) rename {src => dist}/icons/mono/os/sailfish.svg (100%) rename {src => dist}/icons/mono/os/slackware.svg (100%) rename {src => dist}/icons/mono/os/suse.svg (100%) rename {src => dist}/icons/mono/os/ubuntu.svg (100%) rename {src => dist}/icons/mono/os/windows.svg (100%) rename {src => dist}/icons/mono/os/xbox.svg (100%) delete mode 100644 src/icons/ua-parser-icons.js diff --git a/dist/icons/color/LICENSE.md b/dist/icons/color/LICENSE.md new file mode 100644 index 000000000..c025cd011 --- /dev/null +++ b/dist/icons/color/LICENSE.md @@ -0,0 +1,3 @@ +MIT License +Copyright (c) Cătălin Mariș +https://github.com/alrra/browser-logos \ No newline at end of file diff --git a/dist/icons/color/browser/android-browser.svg b/dist/icons/color/browser/android-browser.svg new file mode 100644 index 000000000..92d59275b --- /dev/null +++ b/dist/icons/color/browser/android-browser.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/avant.png b/dist/icons/color/browser/avant.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b715d8b9a2c8c87024344a333117558976a86e GIT binary patch literal 21415 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 literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/basilisk.svg b/dist/icons/color/browser/basilisk.svg new file mode 100644 index 000000000..64e1f61c3 --- /dev/null +++ b/dist/icons/color/browser/basilisk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/brave.png b/dist/icons/color/browser/brave.png new file mode 100644 index 0000000000000000000000000000000000000000..06fa477700a166c3842b1eba0e65dac4d4f49efd GIT binary patch literal 7114 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 literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/brave.svg b/dist/icons/color/browser/brave.svg new file mode 100644 index 000000000..fb5ed8b82 --- /dev/null +++ b/dist/icons/color/browser/brave.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/chrome.png b/dist/icons/color/browser/chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..f251ad8e91a422f0c0c118b4f53b39d2a7680df1 GIT binary patch literal 8883 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 literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/chrome.svg b/dist/icons/color/browser/chrome.svg new file mode 100644 index 000000000..dcc3b1106 --- /dev/null +++ b/dist/icons/color/browser/chrome.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/chromium.png b/dist/icons/color/browser/chromium.png new file mode 100644 index 0000000000000000000000000000000000000000..7a3cb15fcb2247674d9a9ceca46b8f1e0fc8d722 GIT binary patch literal 5190 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 \ No newline at end of file diff --git a/dist/icons/color/browser/coc-coc.png b/dist/icons/color/browser/coc-coc.png new file mode 100644 index 0000000000000000000000000000000000000000..f917dbcaba869554ee23b358d436878620cf84eb GIT binary patch literal 18330 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 pZ&4&fKz5o!GpAD% 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 \ No newline at end of file diff --git a/dist/icons/color/browser/dolphin.png b/dist/icons/color/browser/dolphin.png new file mode 100644 index 0000000000000000000000000000000000000000..d53805f980fc79cbd38e1e3c97fc19c9f1ad7955 GIT binary patch literal 5689 zcmV-97RKp`P)d~3G|H<5k{CsH( zwKQ#+L!Uh1&Lo+>@9}%4VVGZEm-$a5VUUD@e+iJuu!MqtEkGi`zZT#FA0&xn0a6T* zM6v)1A&h14uZ72eHb?*x?b^yYS(=tp437oWQY{^+et$vMMy0zc{8Z(IY+IR@RRRZ+ z3u-p?M>d{I)(5V?X=JXwqR)=+b`+h}$<;jOiET(h*yjvT2s;E<73Yjp8CKjd95`5$ zm#J5%Iibbb0n+^OoUF|WmSIYUw%Jdm1kqWWWj&$X9}$*4@g*vKk>>^11NO&^@D zx}av{up$LvUq)esz_N=Rm7aot$3le4@O6*=_iXvdg?SnwpHd*z0a%8xU2u6;XhZ=A zz06Sy(jFYW??(fx;(e)kQY9?Fk5>&090(YXjOn$d_|z9?cADjCF&H3;fF>6LDrOPo zy)GV@oBF`EqLb%sr3#wC{^o!|#09m%Fdz$1XsR!RBMMUJUQW4h(C)8Ph!I4pq(ZF8 z1uG~zR%0fhdpRO-O?@M}{*blzpWMUlVBl9HU{DLH15kAw8>ao7AOtAXN!noIcDw{* z_u|uucm1~@Q^35)kl#Q+=D%n7<#Q3i8zJNcHvWGu{^tFRA}5fBnRwcz zhX7U<(F{ey6GT`MIU!FytcipI=y+W0jUHvMO~%FD_*>3CxEMdS=#b^_X+DjGln4L- z?GQMpzyYQ#Me7-rMXm``83N_HFqP3P*T9OFQK)G-Bc?7K79fkS;OrGH`vsRZ$5vY4 zD9^E%6*?-)T{buhG{Bi6AMgUoLmPYqobYsq`(8_Vv^Mo8LXJMZ-H^k{g#FXFPqTdeQ$&)Btj4W zzrm6?Cid2Qg42`~>rr-(p0-#a9E{!n`B&#Qo83l0$^zV?dqE^Mq!9u~4`CLdpPq2v zoq?$oLRhqGctVjxzr_o&m)VIAtSvZH=CaCZR=`4YUN;I^KCZs-lh$q-XHC{c%x?eC zqhUQreJ^Sg*hDK6KpZanuiU*u_PnQ};rWw(`NC9cX5 z(Ra>G8AjZWm&b3z9F)4MM(uxlO~HP%T<5!fHHum}tTyz)<3qwTyM6B`6CNkMUq84V zzZPhA!8!Th2R~(RvG|MwAYf>9&rcE_sI%Tk9zH7OZg019)o?&|7iJ#*__MRC&2qhf z$)p8pN&;e54fcw<1brLtSdcnqaOCZPJ}IxRLh-Z?=VW~T!Rdd%7Xb%N%K&X0oL0TF zX60@3Q$`Pox`TMbs}p?!J=s>fGpI`yZ`)LK2y#s!E2hUk@l5+c68c`gh&boj=^uS^b{TNc zRAcU_-RgcaG2vm{=$H44xc%+4sU*}O3HX}g!IVR9eV?`V5)N=-}SoHY1! zi2J3Wi;bhIY+GTjy)et3U*sq&ag~>Il@8u1(nEo%>KToR)dwm};mV*0RZy5R2o_s= z&u;$lFBhNuw!y^#`Yvll|L}Xro?IOj_EOw)?L`O6kM1i?KU{w7oUIT*xAJy!DU^)t z(eoVq2plgH1aTbF223D)0m7qJEz(ajs$ytFj1;r+6W4aiUfSXpA6y?|pdhecD z;Jg68Z+!zYLtW`?@{VN8q}W;VPu_;*1)DRy(eZ?tslWlBrXMuRRiOrb~<7%r*R z4OP6D`R-@uegzXJ(_M88%SfCG0w+CDssS5R~+R1pY7MHuKpxX+}!CPs?upF)oj#h_7 ztHQuvRS!0;r#h?Zy?g3O>vn=-l;Hj(${{!a z!4O5z?SVZX48G}Fb5HUPfGIrEVnhKgI9YXeSIMD0rRhg2Pv<)dp?c&nN3PW~Z9(D~ zC8NQl0_CRm+SqH&oo}-AXs3xr7xe%k3bhg=_;1n9F$bRm0&v3!@F;#6SGnlKr$1&d zhblu$Yk&>14x!b`SH-Hs9}T^;ci8=*${_M82&W81UvyTdR~2sAP_z@CfKtFBXgY*e z8mK`8_e;Gb^5{Fz;}D$S5)F*Dt1;#A(7W#pyk1EwJXh5!ICv^!{*T$qj4Wx@)rSC& z;;$>*`sVSEj#r(5yb9ityf1M$p$g-;n1PRn-LGS`BoP*50)yPp+j#r>qV2!ttOS=; z&Q-|~tDxn?8nE|G0OUFHJn_1X39JX+4L&zX7e74e@rOe0017>q*TNl9wmtueT@#@I z0$5a5WS{P!`f+XRMQsVwU&;T~k-PrLz@nd=#JCTvimK>i6j31x<@%7{H z8reFPW(d3onfTmXy!(Zti%wg!jI55_3IXp089D&{NXT7N>)$)`b!O#B00pW6GBMzz z+s!b>rLOXB#*TB7=frEGNrKdpBLV2;?2Ng3R(nNtL?zC$=Q9`lmAA&k8h|}O795D3 zcknWPSY+?XaU&r0B>oprAYM4V;@H9yA8sn%38gQfPoVex-(aF3q(jd7ZrW?zjcMTD zTCy(!&)Ij+_@kAll#~+7F2@0SXyCiDfmp~s zKRolz=jVU2^LB{CaO{%G`-&XYdRiS{$RMVYuL@I!{MzF~6Kf>)-eQg5$LznJ$(#qk zUOpW{$?I={R8PEPpOU%VPWjz|x4hipKLK(w28v*jpueZ|;48-#AE|(CARWn$8q&OK z9AuCVJvf9frj0 zh`yMEax(6n)1Q5G<{NNOuyYBN!b=)>ZH>9{7~?`mQ3q{YS3|0}f?9He2t33C!FMb< z^|6wc^SE{bu$ai(cpKQk+~k+<3A)v-+Q|J26BRpa%X;qLH@B7SCW_Bvu5oIN8+Olk zC07X-(=Ytt>Fve_$}KI{{}3U{aewd~U!DKSgyiU!f7!?V{GZqzch zrbA2_SC*_xe7obD5y~)vKKL2(%fEB_llRVi#!{@F)qvu=*L+0)sfv;3c48pgF4&#C z6S{S`23$WN{IToJy-46`Et{7lX}mk=*0qHj$Q9RU1Ze2~iSv_R43q~DcTXN6fIOG^ z`m%!62F8HI4anwE8EAO8-~kJ;sSu}*xzTd%y+L;*YU3~MQ?;(WngT~r?>)mIwGlfL zH3%wmm4kC9xL>?EBztE1GY8AljbglQw##J@6RHS~)5LVrr*<=TPSnJM@wGzey*Ze2 zA_>@hs0q&o7eh)zR1Xyxy0DY_8 zwf`>b=a-MoHL!*nQ%A%L?i`e$zo~fZlNrz11iMOXNHlZm27KQebmyelF`Q}eF?;nYg*(Mpy7ewdy{oZ zo@oK%g*=u4jCcibujuD7e|V(CmaQLMkd?Rg**hf5mgC;CmkHIUB3$5nf64v*<)d$q z4!)Ry?O$~hAN$@nf1$P?ia0*V)8pzP1)zR^qb}_1`NL8doZZ@(;6{Un|0qR1@ zDDp?>ILyKMkiV6J! zum;Z~IUk8Wwt}zd9s2k)33Gt6X8tva_uEyvd+ebJVr#lu5WZ&f^<=db397ZLigC?1 z`%~bTZn4}nFKMpZ5z7Q1sX1f7@v0L;_75X1hMK=Y1D0_s9LlTj5*+7N6Ge?88erpEP*J}oE(7b6H{t| zv_-GV?~+8ofy*Q}ZZFyTOy<)Ik{8m5ZZ-}mw;Ke(GK3MvSLwH@5)L32TvN3Awd1cm z-~Pqs{d6ok8;sG_{cWlO=3xBb=iDF7a?_-ksm)WPahS)LCmm@deVeL)d1&;0eD*^# zYZ(wei1fjHJS1V~u!}Tst4~O5w(+)?5?_5F_+j61KvLd+D>FtO974$m z1>P|LUyb;S;aADXUp?V2P;a)}<|7VBccs`_=!71#n0i&ug05P@w7ZWaJbU!X#mVn> zH}>)c2gKo=vz-MOM0#8@DF$4M4ofl6!h14v+R~H{I_f$#IR_-oJ!L&kB5-xI(T!WX z$gXao5DhIn*s*#m<)cJxvN$D0H*Gs;_`!aemFY@K1qN50fX?C*i36KO9h9Bs|VXnsMAO<0sB!Y}rF{)x$!HkTtz(K4!zDYR1SU`y0Te`EHFV!;! z5&)zaP=g?`K!XNRfirhn#+W6^z(KplbAZGWZ_|afG|~Nm(82@fZ++t$bC8 zB5ZMTW9@)c0O0z^9v{Y$XnO(H9ju^QLBuSeW!t2W@I&a?|#j4&tAo5o-GaV|2^*)at8}(Bj0nj?vHU z@O~$K_Xco4QlN6KblAa13Z3~ZVqv950#YQnYvTZ#XmX@{C2{F>rdt}o0co|)JNewt zxnCJrqsI$Lt%$ldO%;^FB{*kij++zz#=U`$!V0SYnAZ*F+y2P^4r2k;NOSC{Knd&1 zjttWP_`0HHYDSZ$?_QtT7IGfav&MCWe^>GqU_dprB5vD}BmsRn2r(AZd0fGj_YWIA zCH7fJE{Sicdn9-&bM&SQYqYdZka>Hd%fE{x7$^`MI9SgbzRUS=PUcvMtKy9md>xBn zcudeF37A8u5#J*w3C28(sBXNlA~*fq3kgfxsof<)9VdSylriEBodTfU@Ip)y9&-l9 zc-ne&;*s97;$FKW;2|&$;qo_+i1e7*03d+O@kv=wqlC1KoPzy@V-pWo>=_p|8}_-b zm>r2j#E7VW6Ig(ho-!28Qre%gKR8^zYi7GQQnX#j{9W}ga3t`Sx{5iOONo>iW2qI< zBqKfomLY>8;Djw>O8OIhL&ppZpP-?2Km-0?YanUszg7DM8LZ1{NJH=3nIIwBU?KOF zLc1+aoj4?7>TQ+>8N`xSC{RJ1Pmwc!{E>%Yvw?IYsuK_|;n)}<)7N(1=EBYtUAHGf zM%-Y!TSaRw_JT@YIWc8t>3TI?%UzOp?QAE3*c3ES2q7C^#bYi?72ipJO}HYu(3!in zWKDrHM@?zS-C+^|z7@Ck7w6!df`i-?rJ~d<`E$Cp2)cV$oCKUNtie+W6Tg2M0%9e| z{2~7D1xX_Hf+P!&#Ge)*i63rJlEnYbI7y^-v}6I2NI_UiLnV=Ee+qP}n*S`03vgXsSN~N;juD(7R=X9@RuJ-q3 z&Q;s-9K$)D`Hq^@ccfH@P&45RndCDa zt&4^#svRw2S>`G+7T+iQyoc3{bWk@SpCtP&s_egyU^|iE~ zLRpQ0b^To0Y19eS$D+NTdKdLh+Pm>F%aF0BIg&a=e)eTgbhxK$zmQTL3e61sJBr_; z{)qYw`~>yY#SE8uO%_yF|=>gBYT+dJ#+3n9m^=%bOtTZa7H z%OB@(O~zm8&|w6q?EgUVH`L#6pnnZzhOSYK3X}>$#VmrYfR~5DVzVx#>7sROf!cee zUPwKS_F|k4&}SUFZaOT-M1JX&k94@D9s2E#3MOcoz6$?9{U7y5*eeC72}N~OikSCM zpjNe&2&*+)>$4P@CFwUSX{ZyTJ%@V4WO^?4;VXvyKAGxp5+T3x>W4U7GM@WK3-D$7 z0bH5-e|%@905u|1ht#%quhc$MABySW>81zb{jk^X zcQFN%OF8-V*F3E zAfiB-6IL~a1Z?jnM9{YHEov>KPmxX&Eixqe*YK~=o|~@Sx>a7nVCW7FUD|{?W&}9v zjDPBiU}XAA+%j^5VvnFk^cZF=>!j=wVJGygBq(&GSOP_-G|??;^B%FOAt?muNGBwj zB305E{$Uj2KNc9+)VMAfJ2+Pq>;owhxA+l8h=vLjJbO(@tgkY8c0lh?G z3Vq_LiGPcBomSn$6hps1U_5F%!0D&_Zy%;GHSM3s{qYT$kT5`5Cp$$bCF`+}CFF>5 z5HSj|mY^YQu1mI#gCq;m2}z_PiMhrGzQh>y&54J0Y5%6$<*Vg{zMqD;c+UnypV7sg zQ%=6z#%4;pd$<~bu^6_&HpAEnZ4Isvws!-yB)fvXTLKFaN|RNcB!mcwAR!1KOA&@7 zEvcgZHeJ0Z>i?_ql+x?_u|sn)WJp&o&YXPW2 z3ck5Ezqgb?me4^8Bv^_-bO~WU>QDNi-a`FxRX)^r?IM+6=u$4qeEQQ@3pz~HRdElX zCJg4-EyDI1cFVy5I>EMsVF^l>B7p!^WHre#lbazfb_rR7MhT=uk&?}IQhSMg>W8A< zB=v{I8~e->9EKyHHt^j5w*@rPgRQv90iSxz*IQSp1AGjWIaxs8Jqp$W5azOgB0ysO z8L3rgSO^rN_An;(^--^;e!t2qySWn_J|o~0pS*UDiQ2%g0(2J1!6i88(~Sb=+7c&7 zQNj6pv3Q4EVcip8{iLTzp)5hyEQHNlin%)sVM=OG>ZR206rZK$EP&kF(J%iy(zV@? z4)1LK%*@Qp%*@Qp%*;%~JX5sH!}OS$>V=s)ow(b+S7&$9R<%lH$4Nh`Rn;why3)XV z=X~db*7Z$=5}>CZ2B)WktR%5=^tELyPcCDr{Q?X7&#SlyTgpFjRza zxkhC!LMB8eMO89xg@Y*32}kf7flst`&r?-`Mo&WyaQNuYHb74J{t;@o5!)ca9Jy!4 zJLc}qQGxk{a7Hmdc`~K^OY_2jp;n0^2Pi^BeitQo$OGXw*nFhT(GVPg?l~dAp(8(+ z!T)67wIIN0j_l8WKg0KD(B!%aaouB18d?w4>-=J77yzjxff3XpA`_ypiQravZwUPF zk;6xK)~K&;Tg{?InC*^(M}9u$CJ*r60Duk@!90CKfVY2guAH0gk5cYya=6&5!+?tr zV(O%jx*{lSL?ba4{*Zy%*8pGB?{(gm0u1`w3m=a>@yjY?=RW~^fEH-L{C_yMK?;>- z<<)h0S115A5df7+P=^9alH~u+o^Xr&)xa;0+wjL>8%bf)b!>O!a_=92j|ax!9ydI^ z-80YqC=)d3cL8>mI-wv?*qi|} zz$FR1`Pc?0oD$f8F$T>i1Da2Q?Z0z==98>e02!$+8>?JkT2hD`$p(HhnO7!rv&40U zg2p$L`29**?|%)@ftwov-Zlah0%{r*gp*0_nDKCfZhf70eSu@p>s0oB# zL+tkm3w`23f79+o2*UQ{d?rPv>ug0S-#ktWxRG}6Zm!$@adjX#Lgf>6KEECG|LP0cP`W4vxENbm8)wtJ)9D5J8&7NpDr&i z?Rs6xc}0?n6qMlCiDX?Zl+C6>1;9lp;FD~=^U{_6OC{!31l)yMwiyOeEc+P$Imc4ze>A40)#?DO+=xO29r>bFI56ATB9z*hs|icV|00q z%O@@%o7fBVRu)-!-Yymn?4`eFg)r#hw%b$(0J&C!V$HR^xdyY2+vPcVZsSN8rnH8u zxlo2;R#7Qnkb?k=-(DT|UkS9!{q~v(VUv=WHy(cCSHcAF_W^z$0Eo;+9zvk$_vl7r zRu3NI;*-a?eCj;is6#OldOhOe4hTY0M!71lw3TsC9m%96HXl)m&UB0JBA)U zN`L19OYgRirT2W^JLL|pszRHS$hqF^a$SRZosVF;{L2M+pqUj#{?Aj9qG}dF0Jnno z*|{+KA>bc0Cn0Q_`AHxq!@zF=4X70~Gd+ILqg&tL>|=*G|M(Gx7cQd^iTys!a>OdH zN`uK>U${CIArLhH22|!2iV(vlDHfNY>lh6;*tq`)tB)LI@m=?@;{)D}!Tz13)Ipm- zb*!JgM)QHsqyGMI_HdnBOeq&#m57j)AU}DH2mrzUrQv#fKCsd6B>~=+0zB~euLN)h zd?@f;0N}>;pA6YwomXmiq z#+5S{3B3VvX$Q(EA{@eG9UufUqN;96D%%C%H3?kT*ER@@Cw2lCWatg9l96tFl?cOQ z=h^WA2Uz)#cSF61$74_}{jQ`x&G>+HHt=$(9MpNk4Djn*fEj0~`VW~{1X(2cC;!X6 z@9|I93<3Y;zISKVc#r8dRP)%ezwsDid-ty8wR*$O7`-+^T}QM4VlX$XRGZ!kw%S`mzr{@|D1Ugy}8f z=F2|(Ftf%zk2nAc_#q&-e;W|CY5EPr^J^S^$HNTIt`Qdp1WkZeK+?q111?D*;Q*Ng zR;dJFGVpm_CV#HJ+%l-h=xroJwh^|`)kkDyP;Y^_t4BK=a^_7Bv+|(_SoyGbyGmwK zs*scJrg8)-+3tcmh0Ob9M-bOakFd3W*GSxdx#s&6fl<+Xc1@5>&B2pLGHP za`B@f4xqe-u(S+qpVj+LFg|~o-JkeAs26bCL0~rc+52Zd^BYAj5=ECGE-T7xLNIer zRM+snpi6qMF6p2|oea{9+radZ0?-+KHE;mvfQY#Y{+S1kaN>?9QPU9maRPqa1U&?@ z^g9SCKn1=^n}QV8o%+0{mcZaL3{3TYCSHIt2?`=Gd7Pl>6L;-q{rDPZ-}nf7KK*@( zOFi7Spm{$GkqWTtbEZsE>$va9+TH>lmtmq%Jo0*)Isgb663*`gYJtAwuK@nOOa|v( z2r>4=`c5VzgMaFtL!7+pFmcd_5U>JV1%5&TWji2+Ex-$6plP6oEyZgUO9ojVz?UFO`I9%JukzBh4svw>9R0IJo%6I;fZPynBa zVO5>JYuaSou|Upmw${$cvQ2)M_pvA_lefMS`1=@~rhFZ2sOA6Rp9|mwybtg>0GJR5 z4X5ut!ihT%Uyb+90Ea*cu0qVApiqKR0X9jaBKfl@%0Sg+U)=<%Br_l8%rn@ra+*=N^@3Y^NFbKFypy^&-dVh}X%bv8a}b@w13>`OsK*@voR$En4J9ap6p7C3|E7mV0?F!OylSX8_G00|p6ffV@#3B1eT?0e?L3KLi2j z=7fNh06<@bP3T3foml1AEr$vHK7`HUAV@V= zT}Hd|a_W!Qob3#L0k*J=C|LnE$tEB^8TcQ7u6ZNq?k9dy$O(8C>`zt{?fNJjeaAr{ zpxA&^fu7_3bP{BBkTNL>&=saMd8RPGIIv|-*2M=E*pO%E*dGuH)S2WoQ2-&bdRvej zkdvf9usg!?B3BL%>Frx($NMjnMrA&bW+s05jHvSgs@YI`^qlXVDBf3W$cM`l*vNp* z#{z^>v14U|CJ~`iE^-DTfU*bRFi8MYHwgiem?EiA06GYgt)L_vz$PhS)5Wm3z@-O=4E9Hw zoe{SQ)k@%r*DI@l%RFGZB0#ZMowF%D-1RpN)J!N6XV-c`?t^{cy~i8X&7kJaBee)v z08akgIEY+)@`5isv3fP`+k%keek6lRh~x?&tAbQ3fdG{t&m%#R`5Mqx1yGZ~g0f^( z_Aw_(;1I2ZtixEZBXAQbJr3a_H~@Qq_jTYCl=v*e`#69S$qjhDA6D;QXYZ#hRN!yg zCLyH){37gB;1;r9h3jHpkV$~6Ph{#!z65=`VrXI<_|Shp-VdCXq`cjH*nzrD{0}X_ z{fB3dEGH4P2{5kPSg*CQ&kf0ZN%MTJm$uK~>lxh4UCGqBkJS0o_EzI-GZ zLTHI<+;vF)KT7;v7xh0OZa|M&qqlyk$LPxE7=FruFI55-XxC4>86F<7{9chXs`3G) z8Io^IeJ`+lSMO6Kf7velIWH@OYaSTNApqOEfPV*^#@-@8$)5WT{jdO!*T(OCNg0QgEb*GNR?#N@WUj3JE{dS<+_xm@; zu(AicO@(-Zzm(MkTPW!LM7z8E~lc z0aPfNjdPVylL4T%?K$7G7{E5z-kr}C1Tgi*k7hA`f@?y>m zuvw-WO-UekodMFwAGUgU2r`PX(ucr=h;&dLjY5IzQ`e=0$y8FK3kUuH3`aU!*%jgE5PIb<+K6b^K*B-|CKW> z>NS{v3XgYfhmaAyei8s^J_!JHEfTmxgC)9Chtv2S|Gi;&NPx~1GiKjT$ z{>OhxJHGhK17P4g-Gz@Co`@el*uRn75;FK1^h^RPmEdo#tJ09H6dC-|>*cAyBk-&c zfVap|DZwNJxaLOVWB?vI_Fa1auO|JUPyhJlV_*2TckecY`cah9UzT>61UXSDNH7~@ zutZ+YBLMg}eVqtB!kz;^0N~-JM$qy!jVo_?OIrH$Pj7&>+vxqk z=tA!uZj%s266mrUSlC5g;3D}`VHe%Yyhj3sk_5(uJ*zn|LL_i^X$ z;n{Y-6$lDK0Vv~qK>{j|EJy&L8sw5BH3T*bdTQsPVmvfN+2{~{uY4HB%YzsuJ;TuIzRa9Zm4h4 zt}bM`hB^;OQ#*aQPzu%Wt83#1&&vEWVMI-YX~|%)jp0;&K}~?Q6A6IfIrem;5QxDj z$Wku*^56Ok;O3D4j3`L}Ak8Pi)UKfu>MxQGK16rdo6*9~M2G?DUf?DG;29&p<+t71 z0RT7o)86+Ur}UAC1Sw?DROABIWG+Zih6_8}3Ca*acR2+VqX|(cmNCBoxgr68n7+@9!^2uqgTp6#;C44?$J{ZxJ9fW;6@{Ag`DR3Z}U1k-ZgWfef;?MFP;AuAxzkFkGX% z{dFkxAof5AMUd_QK$vHExWfQ(FuDbINEVb{o25T{cm_X#jO&|37?(uIN>Biw-;o`o zyj)r)fo15(c7B2H20)5HN8JhRx&{F7BH82fqfmbNGlK+Rygd?JPs4~ny8BJAu?~YB z03ZnkkPh)Ijo6G0;HZOAf-L_s)Fq+voN#@iL`AbGR|lHx<#h(0Vhv!|NniQ5zgd@2 z-&8q&&H)kt8DwxEH-2*LM*uTP0H9eU03^0gf@CyUz>nR9PaZ)_D`1!aFKy13pS9nT z2`vDEizorstS^MJ^xIh(O36<^CH+ z%D>-N7xOa-0Hm!w&msX}Z<7QVVuLT9Aw7C08Z3hanV3?Z!?T5dQDL^=1P&$e_2Vq} zlg3aP*Gmtl%n>ltRfTE~uJQBbWKfF5APGPyUg{}+B?_DwYsX%mnMZ4 zpaJC=vq$uRdBB#l0Rc<EK+`vNs}c8x$4)l^kfm1?I0$|A<|lpjuA6d>#;|o~Zy22YM$1vAk+7fuKu; z^yQQOEZxtq1Td=_N`(0#P*AJ?55!=M>`U^GP)t~T21vDl%E79JDgo5Zl)$`Ju*Irj z71Jbddbw&NRVZe4iJ1WR1yZ-503Qh?WYcb;SU%N9%_LYY1`;g2oKPpJ2qsb{GD`jo zd#Y;XCGv>nL*TwtK@~u5rUVew7ORFmYHeMvd5hY29qYTXwD+5z0oucX#3|&V-?OK} zBHCKB_++5B5;j@Ma^Snn_j$`10kTveS`PYBk3j`NB1Upp@@tTotey`rftgbK#8H*g zLQ2&wE&-Cx<*%a+^8=q!^WZntRMlf4Np}iq77bJQcA+s~Ua!_Tt$fGKfrJ8@uo=+w+R(V}b z)6YR~ZL%8m2`~F%%RK3yf8?cjPjsb0DF7>A0JZ#nrJz>4U06F!z4m!r`f9an zH;Ni&-ABXhFxXB!9#9{TCt|>4hk4j{ykW$Gc0Rd;bka^9^wrsbXtiP;BYUpp$N|=N!HS6@ZGI?h6!Ph6@%>oXX=(y;^bhe6 zvM3n|b<<`8)Up(bUQt12+kvQ=6e+B@EM(qVUJ@H*4gZFp9a<|aY{FOyKq7!wLX`p# zAhnqU+o&3bR8xDeA)y+<(hv!1-bvih@h}-b7Ejp8ZQ$YG@ybC=)K4&d4VjdY*lg9w zrI>XTp@&*=u#?$BMTIB|kZfQL&%)=z)Vs}LTzbaEsu-;$j|+mJndh9tgsia z!iUe}c1k4>hT`x6HjXo{uMYlvyo@qq!hj#fm+p`N9jF)3_mJ~yS%S^y2&&Dq!At@Q zf+7$oDg-Q~Mg{pTe5XchH6)n;ZcYY(0Si0}AC8Weu(<@=Egd%w-$?p=eh|QN2C&3{ z;7jmZc>Z?J0$wB=X#gja0NX1Vp-@K;0+#puRWC22W593WLm9el^4j0{(CHE1 z^~{EUVmeO#lAJ|gK^j>q2itPcmq{}30O=@dP=UYJeRsHF7}|h7-}ASd0XjsUTWYU3 zR|)E55^TF_h@^Au2-QHbB*XISR%b8rA^1)fzlk+$Y)^s@>W}!Y=M1#};P^AhPnUc( zSto!jNRdLV&JIy!a|n6@8I(Z^c>;bsJR64A#-1!}gZi-Nf1YKY{U1Ns&?6U@x&V_h zm{DaJifvU5+hBObQ`C*uv9?Rqpfp;eMp{$VXagDofDjN;2;LK~*s06HM|{__224TMF9%RNhnMh@UN=g0~?ZtzWwds=|x(MZ^cjHr^?~?CLcpStSaw;`(ZZ* zH;+s4T>KaQ2iGRFs#c{Ulf+I?AM)JKvCMNW{Ha24Z@dA!O9iDYqiTmT0fv`9NgX-B zxUOcc=-R-EUPLvZR%ZYZSYX6O_zb9|5IevlzWXVS-moxZ_vr`P(Iad3#{EyM``{ip z7yl2ZVL)&kN3j=I;0<^U-hg8O7^(!_4pSfWoX@t3=Uw#E114OEPsfCzlv5@_Vtj`H zM-R>gFzZj;iU6>{Sjq3YQ;*SVpaU)z zm*W0PP$!pQ8>QnaT0_iH+p=Il#}8nnz84__CZ~Yc{Mx4kaXVmq`!{P9XL@!HJnTb8 z9LHbaC&Z+Tuu~F1P(f?7(dLvbQkd{Pxz+C9++nWC~j8i7>evVon-Sr{OwC{ zck)F13hf7}i9Sez?sh;sbVKP=$rOaGGq(L#IvM9Y|6lvQ5BS^>Z@`~~Z!9K^!d!yo zg3*>r$B@YGGpQ9-K0$U;{Eou+(LV#*q-`hbOD zZIR=>IQAjZ-%!=AP5PE47}=b&pZ_m?{|9{Dh%3Y|;-^xkVuWQ0wq81(Px(AucAcS) z9Hb3^teU01)7bz%>%{s?@~e0yKqVt?@vKlAM+&VHn5e~Ay=_d>7irTE>K;q>Ceugb zWz?1=7>S$61j;XnuM`hT=|x*50XDxvSe76d*X4}!)S>-?5`wan6=DZzfC&S>^2E9j zzrQi8Jst~y^~JFfk)5y7)pV>xVP09BQl*l6;!74i-ynGykmKZB@pbq%m7Nq|o(29X z`#<0d2C;$fz(peS7z>J%Xf8S9JVuf;#(-|-S^ z0Sb^lpjhtV3d9nXULcAZCjg*cy_Az&hfAqny{kjf6$0hT0q-ak$I zKky3%T#SFf7l}p6xNQkQ(HHuecVYcBdPlLm1qecwQ^W|EP?8JHnc>k{-ycAc)Dc~@=E6*3MAET{gUoMHw*N5_cAo>Yy7^rP4`AkVxQbtJ>Avyt4LK)ohh>UhuDreeeIdBmN7&hVQ|K zSW{#>5>zoQ^t0|RyU$cd4>PRIC0LT8%($gx$kHkdIQ_)Bhs_B(f%h!^8Ca1q+JB~WUyu(BtwUdggNQZ}|)` zm*B-0F8008XNs``zJU5?RRg9Bqn&0v6`vH&e%XI7_P^(+j<^`_ zhyTGnuz|HLmyWAgY}mN>qtwwu#_Qk8`fkzz1)@}Lx7)#n^fl>wlblaITb~byT~pp9>a}Q9AN_ z)VEfZB&R(1Fj7vv1po7P2|lHv<9qQW{3Y&&NtDuVTEVDDr_5i zy8yq92Z6G{P}J(K;sOeSl3~r-wQsU^*~?Y$tjz~ZHDx|#-g3ccvkqgGU`~KINr&>v z&#@q*bdt*?--Ukza;vnd)j8+?s#}A7@A1inh6!urh= zeW`Zv2F4TF0HvrlA3U`@dF)gQ5KNUmN=S~=eMpu?t>r2 z*I`f;3k;~uBruGmTf6#o#;f0;rUm1=(x^h2XMkdb1hqo~BxDk!bd?_bdGw#bTcOmg z-ke(JT<~jdZT3C(ljhBv@DO}2zCzhuzywM|0hD4cK^5bgc6h(Ft6po^bEWFjiHy*I zDpFK|ss9NEilMGaR_#oi~*X`>xTZ4H-t-NHqWy1*JeODHeh%1b{#y=?EDm zBqCRkzr-KoQfO+3+g9&kojuC{w-xq3_LJr!P}cEj#aG}Ha2hBuA*Gs9C?KGQQL5gV z5yl%XH|)DgJ9<#{Nc;b@hg1Qe5UM~0P#}_^ONtBtUHjRPJ8dql))X9*c+JzPKCCsxkl}oCqqdp@Gs#pQPVd zyp%TBhrauaXssh|r_>o= z^k?oU9DdLTRjGzjf^8qX*7sl8`f0n1Dw0S?+(D?d?|r&EI@7N63I$balYy2|Ac#9S lHU9Xk+ \ No newline at end of file diff --git a/dist/icons/color/browser/electron.png b/dist/icons/color/browser/electron.png new file mode 100644 index 0000000000000000000000000000000000000000..e17799028cdf2bda86713855a518033760662f5f GIT binary patch literal 4667 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 literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/electron.svg b/dist/icons/color/browser/electron.svg new file mode 100644 index 000000000..a766fedd9 --- /dev/null +++ b/dist/icons/color/browser/electron.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/falkon.png b/dist/icons/color/browser/falkon.png new file mode 100644 index 0000000000000000000000000000000000000000..d7fe06de5f600c07add1c6e644bd559e5c088ea3 GIT binary patch literal 13611 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 \ No newline at end of file diff --git a/dist/icons/color/browser/firefox-reality.png b/dist/icons/color/browser/firefox-reality.png new file mode 100644 index 0000000000000000000000000000000000000000..9a901a445ced22890ccec8c395df438819538640 GIT binary patch literal 16015 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 JPDHLkV1ficQ>6d^ literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/firefox-reality.svg b/dist/icons/color/browser/firefox-reality.svg new file mode 100644 index 000000000..a157540ab --- /dev/null +++ b/dist/icons/color/browser/firefox-reality.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/firefox.png b/dist/icons/color/browser/firefox.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7123205acd4d6be2dd946924997b3231aece9c GIT binary patch literal 15035 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 \ No newline at end of file diff --git a/dist/icons/color/browser/gsa.svg b/dist/icons/color/browser/gsa.svg new file mode 100644 index 000000000..05d397a8c --- /dev/null +++ b/dist/icons/color/browser/gsa.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/dist/icons/color/browser/icecat.png b/dist/icons/color/browser/icecat.png new file mode 100644 index 0000000000000000000000000000000000000000..ad1953ae47a3e0001b3745a883f5b60e9e010425 GIT binary patch literal 24923 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 e$G^wF$NvY?rEzb}#~|_m0000 + + + + + \ No newline at end of file diff --git a/dist/icons/color/browser/konqueror.png b/dist/icons/color/browser/konqueror.png new file mode 100644 index 0000000000000000000000000000000000000000..8d28831181cd4b82cc9963e44d3e777acf517bcb GIT binary patch literal 15036 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 S_^*Wk0000 \ No newline at end of file diff --git a/dist/icons/color/browser/maxthon.png b/dist/icons/color/browser/maxthon.png new file mode 100644 index 0000000000000000000000000000000000000000..987020e9f9a1f1e15830d0ae5f10a19dfd7a5f2a GIT binary patch literal 4754 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 literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/midori.png b/dist/icons/color/browser/midori.png new file mode 100644 index 0000000000000000000000000000000000000000..681d94faa343d95c8ae3afce04dd3a1c4986d2d4 GIT binary patch literal 14611 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 \ No newline at end of file diff --git a/dist/icons/color/browser/netsurf.png b/dist/icons/color/browser/netsurf.png new file mode 100644 index 0000000000000000000000000000000000000000..0e0e3717f80c1de36b1e7252d8fcec62b9527fbf GIT binary patch literal 12702 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 Ye{?7#A4v=9Gynhq07*qoM6N<$f}+7drT_o{ literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/netsurf.svg b/dist/icons/color/browser/netsurf.svg new file mode 100644 index 000000000..67aa01bc7 --- /dev/null +++ b/dist/icons/color/browser/netsurf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/opera-gx.png b/dist/icons/color/browser/opera-gx.png new file mode 100644 index 0000000000000000000000000000000000000000..250cc25292c18a87cdd0e40efa66f7c5b096aaa3 GIT binary patch literal 4922 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 literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/opera-gx.svg b/dist/icons/color/browser/opera-gx.svg new file mode 100644 index 000000000..aeb51a519 --- /dev/null +++ b/dist/icons/color/browser/opera-gx.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/opera-mini.png b/dist/icons/color/browser/opera-mini.png new file mode 100644 index 0000000000000000000000000000000000000000..bf220034fdd55c7becd23dea46812ee7176b0a74 GIT binary patch literal 9242 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 \ No newline at end of file diff --git a/dist/icons/color/browser/palemoon.png b/dist/icons/color/browser/palemoon.png new file mode 100644 index 0000000000000000000000000000000000000000..8696c06004a5ee5284e638f59a7fff044d25321b GIT binary patch literal 23124 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

25}x73--MeS41PcUE{sfV?Q6MgWB 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 literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/puffin.png b/dist/icons/color/browser/puffin.png new file mode 100644 index 0000000000000000000000000000000000000000..f511493551197712e21fb92ab56ad287dcdb0cd6 GIT binary patch literal 15274 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 literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/safari.png b/dist/icons/color/browser/safari.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe6f54f00207697bbda4004288c14f5510b6f7a GIT binary patch literal 21132 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 \ No newline at end of file diff --git a/dist/icons/color/browser/samsung-internet.png b/dist/icons/color/browser/samsung-internet.png new file mode 100644 index 0000000000000000000000000000000000000000..543645cbe8fda19cd9f3c817730eea1785e08ea0 GIT binary patch literal 5192 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 \ No newline at end of file diff --git a/dist/icons/color/browser/seamonkey.png b/dist/icons/color/browser/seamonkey.png new file mode 100644 index 0000000000000000000000000000000000000000..55929783eec1970e46e5d049f90d8caab32a7c69 GIT binary patch literal 16454 zcmV-MK)Jt(P)=?q#dCVAm%#h?!n3ETWnIH2DI!tEfU9XpU#nQm- zuK%6xxo79h(Mr}@-v8#sbx!NfT#a-_^L_PoRdscXNA1Vh9<@hJc+`Sz-uxlqxG6s- zCfOg(kAX?Y&^Dm`L;M*4Z6A*k;PeLD017x}v5PNWF1vP(CiS|LzP_S+C_nSygQEhN z>4*yN~xYfV++18f(q}l*`EvfXQ}cc=)RDC;_a< z@ei?7N|sGcr7w2Q{|s6vv6UT>+Vhv5#zn)pm%_n+rtAMI@MwNpEpJTG*beXk-&_M zRL$5}1MhX%82N0Hn0Eo&obxA`8XhHp5kUaAbZL)0e0cIF8x8+z;OtD=S+=alFIv>4 zNn!%%&glt3j5fQ>$ID3r-*GPv5ikaTOchcc8meZ>P?I#(s z%YciPia3{u|~)Sb36A0N*p)=+>vd(h$G zT61c!s#-TjUIV-(a|CI|Ie`1WNB{#-@{N(7$$pm>0Wo!>qS-P-`kmct{-h~ub(C*#5qqE05(BxU0o;`0azQWj@BBfR0^Vs%Zf`I zfnNZ*Nkkq15KV;)cOO|w}8#WBs?4D%c13wx;9WZSslCLY&>#f+#)BgY#ZP?Hs3O#A+f24yt5n5hb47vV&B(YZh!CLc+ zK*KpTfS)G?`0yj_`I>|O6zBYZWY?Zen-(^UI;PQZi0~s9)a4J2iSv6V**x}f%lOq_ zIhV&ib`XGl`<0L1tbBL3P_V)U%i;31aPCqVtUw}>00+k5i`(D} zcfb{^J@0&$;lBHZcOIPH{{rHUB|8cikmViqsF2&mO+UI2ZV(kzdZ=jY&G*_8e4 znt{Kd1-=6W)zdSmn?9~+pYMgNc+*^gZWxKF;!0=la!?QNQ zvPuMetPZ0!a2|}nntu3$OX1)+;b*Sj3m0u3VCAYl9@suasg$&>WGY!XJOP>{cvc4d zKP_5Re$k#i$Nb=6&%+`?Q&yO7bI#}BpX;3dn+V;THbwAb`Xi@tP&YGiL?onfeSIa~ z{FW!NY2yIjdC>FfZwO8ee|kCm#92^E;Lrrzv>(Rn;1mQvKs6E}F)&ht?gAW{1Q3#f zB?iUX`L~gMeu=84SxoS}tb+K_tPAN^y!Yl|5x{#t&z_%G_hhAW`tQKPjT;xZ{{FJ9 z*F7RXI+eQ4*NZ@!dI|;MZ~pd*0RDg9)`TOeC=JdyVb zP9s6~jKa`Ve9ak)p*~sTNX8m#rQb+i(<@hek^=+f!08Zz@Kf;MRjsOQB#v0NQ{L!$Yc2Fz`S3!S(y#i~-L_UugK=_xAF8zxTD!!q%<;)4i9G zk(x_WwZ`!4z{gZMDH0?iGT&7`=bZn0=k$tZo}aDcB^g^0Ih}*L+10@qX@2ND``Kp% z@V6gR{`~90MJprt4F|U#0xM8VKoyjDjb0zGsf*r!^8wg)463Q}YmXBE{`KGPBbLAw z?`I}-rBZBm9-*r56X6~azOOp>%phU@ET8v(v(fOsnaxjb`SPA5FL|n-29>&#EH8@? zs(Q#7B!rwdzyhiW~2BdBI*LRH6$Jgu_f@}A{{&9HTCgllh4Bz|y zUcUXUU37QHI>2mGl(iz2O5(F_qmnA0G)68J;fSgVpmR`w`J4BX#GIXZ``ap&u#Rui zR8QZaZVu(;Jn;MFEYDxCu#2m&_1t;b@a|{9%3k>1Zb)+QIWG0C4g47N#q{r*9k724 zwk}fs{3^r5c%65?<9bv91XZ1Nit=7b5^K7;)MYh-jo#~ffuB)jZUmU87|>sQ@iMvY zx&v1yiRoLnuHT7Zom1dc26gk_Ghj@p&2#Q~OL@Wbo6-*-zg76Dv*C&BqLh0eAqTvZ z0P$)hW(8l~4*SMnpsc**X<&fYz5a(;4d594{iP6OKGgTFsuYX1Sxt5Iy8mfoxJHBz z=kdU)QUD9M8*Vu8hpKwjs#SfiT(-8+P(eZS%zNb(tGIc;@{I?DKY9WT z_P}>{fe}bu3-}JncN&Kf^ZoHq3{w8|$*^Ie@ZJpktN;E+vOt+QUCs>r4DVe^rKGuL zut1f!0WXGALV#$EhI>=7XnIzx=yl$!J$-|^xn?4QD*WV6ZUx|DHz{id3@_Z8NAQrC zS@vwPPlU0t>*=QwwAE1*dtHc5M60ryLSw#4QO~s1P+bEWGZ~|KIP}nDe|{}bs4p(2LJKj+xhT^ zZ)N|!@q9`6q)1yR7+U?4e}HUh%uZu2au7@oNi?r-yc=cgyY7w=aFyB$kmBckZ`Kl`LEQ?PMrB)Oo~>kd6o|L@T{k;;aH6@_y5ef&{u-%_JRPZpFZ-p1}a6^ zvd{oo)8TpbIG8|WSA;uAM} zoJQ~8Jpz*rD44c1ucO#w1;D7Ij3s(@l_ItYsmiiM%{;9;~kIDM+il7kz6cKQ$9GVagPeAIG zD^@Eds~HIr-u5iR>;CQv?!SMCojXVJUcosjr!kmT9QINX-U}Q!kqRUy1iL-O@FWRnpgc&)~)a7q6?RD+o5K}FZ}%ZFkXXWQ(&iw{BtG9*ZWHe zI47(c0Iw3Bm+bMHKw@FAPl7VkU9)#UDJ7n_ej8r>#3Fz2hZpgxSAHdA26Oo=ML;rZ z#sZCoU!uxy0Ixfd3M41v{n=Xd^Nr!GEbsTKm{VV=o4@jMXa9a)_rLVAl^~Wc-s>6Y zGCXcMJU9}8hdlV3UIiLJ(ZZMtMwEtE>Zye9i9julGBMdudW(@PRcCnNnabZ^r+n=J z<*83z%i}Ly$@jjyhh}Nrdp#j(JGZMty9)5DBD@P2&sE^z5WoYZslK$Qrw|q{y!WTJ zQa2CYJ{|D+J@1u5!Sckc@B{FjU2wq)=r6_Qjwt^#5TF$d7A%a_V0cRNR-o=;8<+qMt5^{d3#o&u)A0Kil#hekaLUqd=qV_79)Txcv4SN_D~x7LvDQqh zR?|P1);b3HonLMdz(1TRK%)2ipDPrM&1A_r=kwt2v<~WK2EJ4N9gln5Ac*Db+dX|H z!Vf`R}ax)4urAA`?*eaC8a`Afgx{jD~~BRIvh!yFn0&NhC*B2{Mb{JfJ$0YOBJwSsf{L=nyb?&$0pNahm!4Gwk}_U)T^L0?~i zBr)Qgp7ufAZ1F)mL4BN~Lr*`z5BI><#V}BU+Yb_~(dP9hT>NR>ON_9%N3a40!5A<= z)j^>KsZ*+{#?-N)Bt~%_R`m+s+$n&~0?|Rf`sD`@AqSRdGncSM2*X-&&S|2`i-0@w zUvM%6&_jo&E)!vE#>S9ssGj~o-CWe6(eNx<+{J~DS;Foy*f}aZbt6orFkA&;8oBS7 z3c>)=76F#_NbCy~NK70EIBzIgWkuid`awPQR8uHf&v@O?04{v&Qi?^>N(E(l?{AyJ zI?pb$RxUIBEV|Wa3OFj+Eg#hl!PhMlb`qiD!Xr06r z<^MDe>gHZv78(uBBzHN`Y}rzl_clEk?m7fsXO8~c?`i9`4NvfB->L*U1I{XfkhWf1 zbBZE3uUG>H=qd|q283;g;q0y5WUH*)b?0FUg`_PTXp{O5ukR#+wZ@5rcH)*rwSC-n>%kVdngcs4@cG|oZG3Hu0B-|y zE(DO)2XwhHV!FDlQ&oG~2X%Ak84w|C^IEy8mvhfv7+SlJOa=K5>EBpA62a;01jvg$ zX?Uy*O=$qXlt9r6#%0-`7cQgjz!szI=oD|(PN@Y})e zBmzuF4v{1g-)OK%RnG^$M&8ZE?B=#?S!nOR`{;$mqD3U1|4;9rZVs_YUNqr}SFHx% z`@3NMf>`~#=Lo1yzxdOx2-+Tvn+V=u51hHwP)lJZBB^xsUGh-$> zyL8je?)bWRJ^AwTYy_>x7Pz=rl86N zEk)N>TezMWeIt4Lw^f8RS5=N(~KD@d^ zT>DRX*0Wbo>@M=P8$4jxya*1AwTd=o`slyi=pwF+@7AD)G<>J@edi7aPFeea|sTiY3d5ENyrJ zP~{vEp-`|MFcDmjbG`+xwt*;tZN*{|7K_TIgkq^cDI4W-AuP05YY^!K9^%pLjz+^# zue;!x@o4r&>v;B*{OC9l+Rpn2Rmq0VDH9O^h~Dw+XRQaCoj+0j7j?(QAMHs0qTHVt ze95KWLw7*~s74fx&=E~Zy-*5Sf<}oBT|}`KFi~%+f}`OTuY|pV#>#AR-$oJwK53fb zor2KBL##EVsatd3{X@%ueJCU-Gg*X4*%}Kab!jx3L@3bP+e4+dJ6yZFN+gAZB(d4! zB9%%hG$@Ku)HgwEj(WH{=h{xq6hJy6eBro7^>EHNNfAgeK3-#NtU0P-Oit#fa_2H9 zfXo8C9e{2gHn_rw9wXEad_K;qOC_PowR?=b)=9jYb-PH^$J@)5W5N{VW|^z>=j4 zS-pBtzVn^i^t<1=y}PTcvJTkiT-_wjd1fbot|*m?*$UZ~EFxVJeqnE4Pw?s_X_Fy9 z+XJKcID(j_)Eo5-#7U+mCoRh~PXqsGx%Q<8=coLdig_?%O@SHE0K&a=FOL zl}j?9m$7E;as~$%(%0KXp%}^0)g?TzeX&=?is%~P`yw*)?qCu8pZ(d-*w?=HlN;Bs zU&iyFe~C02it~bV5!hO_Be0U`Kc%ElpinGOsq_F#NK>U+onm5qgwe4PCMU-6J|}|# zB+5DpO2>9-&Doy;SY}|Li*@T3g(+inw94Vb;~Y9P&dA7AxR*<(2=w%nShA$T;>8uZ zyGvn8diFEd(g41-Q`oXNHgw*3h}>IeHEE}-z_BUivQ@CA&oEq7Mr(wU@OT}{Hl~Nx z2nDM#CLmV`5R=Ccktk&dDy6D{v=Q{Eq~&rc`@5I3Y4aM^uV0bvv14boKOCWExGNz^`86G*t_}EAwMoA~qc12I5cc|S*}5Q;$?nj`eQ*Mvu?a@1WWYu5*M|sx4&r131P#l)P6?}l zVZp~sC@Ae4RrZW}3I?_=lDvg1Cjt(|S^`;e=RAD0+EToFP_V&ei#T)ZI<}s#!KtP^v=QDIK?PKyQt1+7Oi=WPk_HN(d-on)4!W$nr;N2ogkDgXAtImYdwB`~ zKHmT#LRWVW-90@xx2j2opa>I_W7Mis)a!MeOHpkLIH&*JF)*|V<*o{wmMrIj3l~r< zbWyEVIeP2}hYlWQa&ofudo)}6T?W8^+yl!h!m8eQ!UuBzKF@HAmJD22+N~s_)KbZ( zL5c}sU14#z32O|;CgW1Dn~JUGWr>8r1BRd)MJoW@zwIc-2%9&rb5YwJ1G0nbAD0daZRN83xm3b<7!ZgqrX|DyCa?5Kpj=6z8P2i**c}!khiptPK zPfzBpIv4`Lw9%keuhOX3X{3!n29)+?P;8P=C=@9co3fsbq<|Pu4+`{DDpYzatXQ!+ z^ZYRm>_42{t8@PO3qsxhmV@w=^`OLEyjDPG2fqC=3PJL>tcd3UT2Z`8p8he_lI2QC zIAfvEQ&f(Odq%2CT8vqMnCK8gO=6S z6oT@_X~dTScJ3V4WbfW_rn77TB2Om@fMoOc^u?Rm<-Zg7S=Ax0ALJ2xAp^BoDj_lp z!LNKPT)Ip@3!Y9+gdd8 zKD2b@<>S;S#tT-U(ISGP7>ORfdccIV@93n4%3!4gy=YjASlBH9tX{L4zTPJA9`C`3 zV2!jz=^()SdYI! zW&m#nk!`C4Hpk51cd}7h)YrCxQoie_y9dtPXxKLvbN{oK#_HSlV_?nnNonrs?ZBJX z^MZxFqsoe2&)yMbpbHjuNdU~DI3q!_ohn@g352OTB{4BWsFdQgFjn>S7Mm($c>H35 zjgI*6vq>T(7V^YKDW?lvKexZm^t)aui$izhM zRqgXG`M|b476V3(lfZjWZC8%D#1Nf}9qp>}&SP40ZHw#beor+Y^AJbOL0QridVAsAvm{9Nf8S3? z{RYagW2h}1>zwD?fo}uf5T}bB!^(L}d;n*8pD@fs@o>cacdp}v;3?_lk z-=IvU!kLR>`ZivVo~Q~k4exhs@|-=siScnD=DUFe-QCS0ffz}W%amp#|L{5a zw6ncN@1U;_&Ob}Shx{M+!ls4s@P@skZQkEG&*!6q07)x&QUDcfWTdM{);5rw*5QP+2+kJ&WSLtin=tFDAzN{aDHzF5JYJELttVe62e-| zNq{wufdNBzkKj_MH}aNkI&3bah~zu-ERX~~b*(a$3R@P%lJ7(VZ0=Rl%=5Mbd;u!O zcudSd$x%*}RD~p8Fp49nN;Vy2YG6WlkZMc~4cc13qIHcbSxtrAqr%T#01w=s^56e8 zOm|nawx3X`B=q*iYpG-qnT8BZPlWhdr(Qiq7$w+clE7umVqOHTHyRRy4h17KO0(+B zz_d4EluFX7$~m@gud{bgjiI4NAi(A=rL3Y?V%4fbAV*M;HZq7jG$n|+Gb~;VXP+rS zvj6iw!jkX8t{A`>>6H5%><;i--|H#B1H<6FcS0FbQPvK9a$?_CI)r z_X0fTF#^B`u2(=fV*%7$jQkS;rjdGUp#8ao{5{4DcO3LQWvvg=KGD$F)1N@z!aq@O zdAm`T7YG5Kh~eOvqi6(cAP3A5vyU703s5ZuYC-;uiGj38u4JA=hiYMDWXOn6 zWXX~u*@m558S8olD&5%i5Vdj#|k9PFEiQlSlUk*b|=(fik|g|%y%?cOo(KW`ZsaBy_8P1bWSpTYZ$ zi6CF|fUk;#BWS{!)kRjWDr6Ghd_RVA zSz2>mB!)4{MHdJFS6`OKgYN3z%sLAQDr!Z$1cJCS_SqRFJZVMLl7= zcX(cO%0xpj661tr6-a%|Fd9x-(ql+{%oqwraSB~!_|kUa%5^4IC;w?GC*>^NKLp2# zoy|TdYI7t>#3>^|c9l#Wx-*iD2m~iN7(Z7_l6{$}6W9VymA%H)C=|S^CQMC5AoV~F z*7OqGnc5ivF_0vA;4`y&0Cc`4R|@Z8WY}}`NG&<`^Jl$tExyJB1)2Uf;RnQYxCzi zEh%OX7^lAixq{@&!}X&NRIAEkA6Mir{&Z0Q<>R+BL4xHKsCy8gVA?%> zdifU!5`M#en5;Q2U*kDE5vKwq|EM%9-Xtbe0mJJzFN)njse&c{`vtEA`5){x>>F_u zt@5=UmKUC70QkfwCJ;F>@Ic4+r@t46;(gF|DIBJQTlrdFy4TYZI}9)z>ocbdxss* z-stfvVb#wFpvTwqhbChzuzSP@&tFiEW#)1o>;Z~`#yUs2Agt;azOzfIlq{F7B0Dy= z%1t*lGRYQHrBhjF0)2+p8dM$i`ZywTuvF^a8-vN3Cd_OKAd=kUeGTtNt?H`TL7TCE ze|F$!C(O)gpS5u8nCIoMsBqb3MZUaEvu>YQ@?BbihHvfE?UeeC#|G#t!wvi3{v(bj zu63j;aq*@YqyBi}hca20Pz&fS3sr@Q6^b@adZjKYyN~lE7cTXPo`} z9R2+U@8=-*GX{!^F$vY`gk~hb7;}BZwzV@T19>(D5buXZpcw~@Ec3qMRmH;0GJh(( z9j06R24mrG{-%#oS$N$w%1~7}Ye_t-*ZEf9cY-RPNc?`;!dJH|iFhtw<(W#`7FZyY z_W*LZU$jce8a52X;L?Z$f(%W0RNI!e0gXkUj3mk@Z#CKb5-w><|9@Bc(idwv__Km7 z@{lrwyqZh3IwanwSX;gpK-JA@cd!$&KlFa$DcL}z7gaYGZ=VAJP9FRuiPHc3f6&Wi zmlpZb{mtMC)(*hR-s4BRS~Go~571SJ^8BCoDhtbwr=OubI3!e^VkG{KMyf$pJuqHZ zR`x}L^%V2nLK>Pm#%tgKL;(XW@E28tFWez?C5E3nPq^#ul=r-QJQRIYPg?G0PzKeh zsUt>&nlXi&AeM#act%k2ZK(dGbH~KmepFLHPQA=e8wp47M@N+_t}O9Ke^?3S-FIJ0 z0Aeuo;z2rt_s3u_uEc%$e$Bi&<$~o(Ej8JFrP=^X?Cza3!h$a7F9|&bvXn5YfK#C; ziuXcyQ9`|8w5EtgY4RF3oczn(Lc?2r{|S=i=8o6CW;A3h&ADH7?!eEi76#|)83B%p zh`GDbn0SC_9w1S5vwXyzvcv0KEQN zaj|D@Kdc)7C4%1>=?nR;9KyEnoA!CmU7Th=*91QXd;O)@?K3(7VM(mzPp=S;4SD|T&qmp|&xMMgb09MJd42}Z$J&I6 ziDTMmj3Z*d*?!VHKq6w9#S}2ABST=mr+W0o-q(C<75|iZJIs{p#&w0aytSY8>yvP< z|8@I>Regjl-rYs;?b6@2qSxJ${4Z`(44xNlc8t{G%3hC+8AD9_B;Vy5rj+#y;&d@l z14R*NYn2D@^Zv_TUg7!AD|6#s4F#ViJp=;8 zp8R(2&%2glWpCFg;XHsd20Tw(r%cqFvJI+);IY_%-V#_5dW*`rgE5$lzble!q7Lmn z5TZh!>aFY-?m47<>h>gyCoDgC4(#3U`18#3x80x8-!C{fce$VWwe4HrQXL=PXGF}N zLZRyiu|hcJ=SqOqG*JGEYHh^(z5T|lI6*oH@W=rV&<_5{h;rpsC0_j(y`i-Gci#@4 z7YML*3BeNEKG#RkfKVIQJ?yw_mCKYuLZJw00w065xTOmgC-Jn}|J2kmU#$*X zW0J3Az>nqfKSu)O)4=3Du-ABh;Kd?U4~bRPdG_{)mI4_AQ&Y;S)t0wq(vQGjU85YU zM(IC)5Q;WGb|vrJjgk7!VYqRhvbdb`%nhERZKiB7kn=R>^+=WlU7E!Y5}Grs4cI^0 z0@vhVM6!*vJy(sj{kl`GzQN$U;muDI7IwpzzFgz2ZyCcmrBdmX{rLjEeQm&I4zfp_ zYc%EmpK|$sSSN)-;R`U4#R2<8WId|&wrpVjz@Lo(-p7TZx4o^O<;yMay;ixlHSeFj zv6zUJT?JCyO7=h$bJ581%uR;pZiz&F`#UE1*MFG`sb0Ap!55hU_&Lb^^zncpO>2I5 zc()N_?=6>mzX32gIpj~40G?n2{|g7VtL{7l)~c!q^I&r+co-8?zBjzFkH=pU^Zn1> z9+zD=E`;?1$GdL3lBfjt9)&v(de-$jE?=o6R@in_I5MfB+*=O7$GBlx55cT}2P6P3 z6s#O?6$4Zu-_C7C**PM7Z;uTteSh<02`S(0ndHCyt>fHtPs+f6v~@$BY+5&ajUa9$ zEiyE;Q){)+=Ht&#O^wu4y?r=`MJr(8=mZo#2Zvvydep;W3k^UxS({7Vn+JWbcx8p3 z{h4m=KBT<)d$IhwvX2m?IiIaZfHbWuz{5T+9!ejFvEPU76!w-XdJMf?&7Y+Ti0KpX7$?>J&?` zaG~IRF8OmS--^sdA*JK>l4Tk9T2+TJrK2iI^ z7_FRlet~ztr=Mam{@b;CjtDDy;i8qXI5Sa?t9Lt&InG_`!d7o3M&M7~K$s#5wiQGI zUR%MPg~YVHy=@c>p$uG1n$re+d#7RNuw~r>!*5(9Vetpxh8ya9{vW2e{@OaK1h0>c zFY_+<-z|6w;gyr=2?|LI)g2kmDAKU@U>@KFWow?pBxu=`ix30f)7vw*q+I;6#=f!<%@BhF+NcZ0L1I>E+W3+zxni%bkJ1*i2u#&hr3M%x?sexb&2rn7YVK1^6cB|_{{%Sx#nv%cI|Q`2_dH!;iTkV zWUdM+C%CWSe1s!M_P8wfx7p)=R!zTKulW;tKS0u`D<@$-DTsyo-B7$5_Wu#=_OPaT zubrVM8QchYW5?1#!v{Y+z`C`TkKCwy?haVnFCp4LI@LVcL%ARW{%ISa)=-x8#J)dI zjPgyJ)LVg4ECvPOr|KHwfLjkrNZ}4l7z#!*0>R53pIuuLKQZe0-!Inr@>goyd3z%$ zQn?KM{q1G&8G)aFx$k&v!uIoq?%jKB_5rxby3c|<$N9@7D1pV{ zHU6F?9LyZkx{)bI!GMY|QWN%$2@ebjJBAdmCTzq!ce!xUYFJo24*K`6ZSc)+*SPiO zM&?-_YvUe9Yume_PxU-M-RqPjDY0|sZEoMbn{Bb!^)}#-)6~uS&!c72rVnwlCX&~c z?Jq<16R_EdJkE+l6_|aVZ@)^O>V<%B)hf#eJ~+U}GZH?2Q?snvEb<8B^%#k7S?GEC z2G5yG2n`vDXgFoFs~{v{6;GIK2>Zw3u0x(9lZMfnfW-gev3YR@`U+UpLo|iGyHdWF zfqw0Eb?&}9<>*n55kmE@P@vOJNm@4>K_d7GqoYH5*Ii$gB;kR9fu0M1;cB%mfX;~F zV;=Ky9*#{;rUEGNzmPu5n9H5Wikt+qv%O6s^QMk7HYU99eNpy58a+RNKU9U1@jP*j z-S}2xE6QUOf)1$OXvVufO-+Z<=zs@Zh4w-4_FgYt_cA(x01LeDNol4;vn?w^%2igWAtpd!9>@vn|vR zk?B9II07&)dT|%8e|=v7<~7$SU)oNV$~6~j2z%~+_hNX-*+OpVl;_Z4&))r>U3*;S z-Hu(mT>$!Mc7JT#lQzH@NRkM6p#a868{j9s*<$|Fx;YR}HO%|rxZT@t|Eh}AWne+i zGk~u&W|RI7lgB;o+0K6X&zQE$o}w_lS^P8~`5`+Hskyl!Y{YEIw-$c7CcV7{zSFfA3Z7pkV; z1k+P0U8UA87O1!FQ;YldtQG$9HNE`gOS(d{#UFe{**g|j^|l|=EXs$!xI*YI#%7AQ zym>5>Jl{G>Cdzj%;nUgN_tu^Iv~KQX5{$9jb=P;(W5;$Ref^bx27dCEoA(>QGe;A6 zBlBsSH!lt>G{*eA_l?g$*>!b2(HheWG)NM|=%^3Xy*ItN`7hcF+l2*`H zV#kh~-Oio2+DfJS!4)fdt^#&VPBu=;`vK&ND?Y<~+uptZDq~|8oB02R2d_@k>MQH@ ziZ7KeGcS9^GJfloU5JseWA`8Lg7pjHacb)ZGBP|xSk({T{!X30{;N@jhkfXo_kI>{ zn_cebxb%9;)4CZHEP|f_@3WLI%k{fPR;;W%8MtX|tRCn6lQg;XvQP6!*@YJ@3`+3X z&u)LmmM#6i|K_(m-DjtBn`Xi0HD8B&j>Z+eT_ehKHxV{V6fJ!0T!}JO1}5A zgE_t2pZ;mx48XUY08+>T-&_5T9k-aSuJY*O#a+(^zBe*beOTZF$oc1gl=-$>ZuvDy zoj2RIjVj=Kdy`)&my(ZX^#c9sHkCJh4-zXR68F~q!Qu%IjV?nr)J-hEu{s zX0F0bSA;oj)4J(_pFzPC5eeyD=JlEE-)_6RyGEBR>3R<5d9Ts2V_ecSiEd~v&k#h0U#uztYv*H08SER@hZ`PbQezipcf z{d3OELGFKq)4J&uOtIDiWS_@%ZtvdvY$p3dix&s*e=s&y3(Cqom|Sqdywm}*<`qJ2kVW%EcC|Lz~EetA2#zZ`4#tNK*`;tKpZOU3TlJFIVb z!%qGFcUPL;KEW7=_j1x{UA&&=Y26IUlSlevV@LI#d%ow!$A_BWe}B>9(oX>U$H#No zpFc~C6;8%J|M@HI;NVhvD-{p;W8?M5y!H=%8H0lxQn_6?zb-yS<#DeUWX>88m|T3d+EiW90c>}Mx*vLYx~^5z=cvS zFE^?Uz#}4aPV3_1(><;0T+u6rLmabU&+p%VpS3ormrBLo1Kxl1=#*K$yqmT>f4&Lu z31+i@|Bt7OwPLL?K7S*&+<#%EtMI$Uf_b4pW!DJ)!#Apa^FeIG3q`A(vjqQ}7YL7C zj#0(eQunFxiQ1n72lnq7v4DH&OTUtg4ExunX}x(rsZ_SO{{FL!F-1{r%ss6-jnleL z;Ek~%*gJM?uiw7?Mjam?ZuausUMSdK0&ba_YS;w}OCC@jA(IO)Ixzvh^raVw;7vmV z@VfiZq&;KJ(u*r4^G8V{8SrBEjiH~uRsH|mj~%WG17+pBrMRCxSL6w6FaV9ze|NI( znvA}&zq=r{dTIc-Y2*8{ytZ3n>DXTEefnYV8|M{^3;e*qdD`8*(yA(|`LeS72&Z*) zq<2NC)p4Jtcbdt*1zdLT9mS%&7O2*0F5Kf0Hi`ckWP1Cz|F}v3?OC*PU_qty!k(ge znK3*bgqqst^zt?M7oI6{{vZY*b^fNQy8m^}fG+<)Jvd}$kGl5fsjf^W8AU%6K7ngI|1 zG*fo{&xvY!^$oXf`wrlr^!TkJ@Q|a|bI$pgc&|n^^?+JizRLUbb>25Fx3)y3vReE5 zH~LHwjEE7{G!GVYss8_-*2U-AYHd$2#!#HhuqoAL2uB4S1|lE2TG?s`qsKiWt9{PWdn+; z1AIFmr)agVb3cQ{7>laYET8uW5AKj-$M%@1sd0=k$E+=U*xKY>zyaq{QS}`TavBie zjyr$N0#3a?Gbz|F8WAO!3b4PC>Wy`$|1(v0Upji|@GiguY&m14tv6EU>km5joR3=& zZ6iV!C^mlE`}9SiJ%vIqmC7>h?Oo-%yBC=xDGMMmUT}bRdg;SX>)L;3Y%jAx;$xRi zs#8;AerRYPM~>{a83D8jvbFZ!4EPUb<5R#<=NjU@GeDc%pFRZ0RX_lpoT@z+G0#j@ zZcm;5;PBAIc3^^*ZCO1aUe(rW4fja>F^@U_lNQK{fVJfn-q(M|`}F5k-DAL*VzGzr z?j=+zgRZMv>2G*i9lfj_+n5S8GLUz@heq)@QddnL^y`cq`*p8E*{ zhANJN7;LyKHGbLo(M%_iZlsPF89;bHQ>wTK3^(TBvS|?DUxhR(@q#T z(tfzBy5QZ;)wN!q(rR@~GSD-yhh=JNl|;J9fCX~ZVQ}y~IePTo4bG)ks=6n7?=DjHX8^_M zy-5@N&_{1=_y^PBc{Wr;6wo$BFy1?HuF;I#vr!}SbmH>{sxBLfh&g7Axx*OyJ!6t@ zWT0OMjL@=LZNd`Gahh5rz>l_>)@YxC7#LWWdFX*P&ZTFn`g5~!RyNkD>T1wFRb2>} z>A0*)dk{G#B12$~h{!Hu%>5$fPGjut8Hw&~2OJ=a0Sq9W-u@^7P8}I!Ca+t*VnbGX zK2oXGs)L1sDLCgV*%$zN0Si<`WTa5A!`^G%d&U+lSa4XRRGV(PJ9KDgu0}c==#LWM z)SI-CLcQ0A8?zW=!~ohcPDgu`0MjEVl5cN6{@=<}=rC(VTi?^{F@W-a(B#ZBKg^@{ g<7Q4*&oF07*qoM6N<$f@-RfM*si- literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/silk.png b/dist/icons/color/browser/silk.png new file mode 100644 index 0000000000000000000000000000000000000000..161e5c446cc1e5ce180832656348ca07a0f988a9 GIT binary patch literal 9184 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 qa literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/sogou-mobile.png b/dist/icons/color/browser/sogou-mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..33bb210401ad0919f1a64b35fc69bb0fd178cc3b GIT binary patch literal 7704 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 z zAWhhem9a6EQti_0%FOn~f$guusNA}py+rUME}VrK!l@N$)rGPCFiqXVr3t6YhkFA zn-OSkHVHx;5x3%`L25;(BiJc7K+I>C z^`g@WQO86a)94RQ8l+WrehYWtg2uKb7ZhFo4pFyLaZGc&$0YRsJW0Fk9M-C%&<2fd zD{WA8cmzcEK&bKzJ>v|+1ge+w#B6~G0JZ7D8j0O;y>5LmE$;#knrVg3{3 zNNS_D!!O&X;Q@W{Kx3QA10R&#)C;x~ME7MA=#FF|9E-Z${cb|PvuTjK{pLfrWRC^q zX(vD$+fZIe*#$ef*Ka-oVOO`iO#);w97~3Sq(P`QVm|YVc8mvC2)W=v02*6U0eIko zQUK^+yf$J#1A(ZcrQ%qY7b7OAKRPK8s*jql-J&@gl#H)sqf-Rng?tE>CFX+{3P6dN zH8W@SAjD4C4ajnEtVpw9Q~F0H0YcA)-u<{$FoRyvocBxSQBVp`IJRoc5}7_`w#*n= ze*!}6fL(yB1ji~Fz70r-9`wHZQGM8a*%~#)cERlN3g!}A_KW5!-0)&^@&T?gE4YO0 o_A*0e$xK0r^d|?U8 \ No newline at end of file diff --git a/dist/icons/color/browser/vivaldi.png b/dist/icons/color/browser/vivaldi.png new file mode 100644 index 0000000000000000000000000000000000000000..64f0d32e710cda6a85c308202c6318a30df6d070 GIT binary patch literal 7634 zcmV;@9WCOCP)$}(8>n&S~7WYt!h2WIP`F`&u@3W`# zBrqg1L+CQk^LsL-kj$L_|GeitHVG{qi}zSMmX4)kXye9$1tzqZ$(IY!!YqVkDV7V? zzYB#tE_mlXq`!|myN87;sAXCxITl1AEQd#>cpN-F_4;IZibp)j@!pSuN5XOsuHR{l zP>=*Gc;*{?6g(H!!x?ZrybV4H--KVluiuXxZ-HyzR9FMgg5`6Vx^S@@pfJNtJ`+xcx55u$E40BF zn1>0&&FbrlE5)g-2inxPB8j=e@A_H+H z%{?T*%)(&G6d!_;^yQkr1}Jn~;9B?+v}6!j3&s}EJWNPB&645%YvH5ta)%KL^34Et zs6PSzl;(zGkeF5IC6XFB9OV>)J7K+D=J{X%J9B*n{1%Lbif)$33XPP;;wSJ-ucAS& zZM~kq9b%3)+<4AXWH2eBaIIYtgEoMMpU1%O95vqf$`W=Nf8SXl=Swc)KL_gcqL$kB zK(V9jf8a6MnjqV-|9t2I1Jg@_EErn$`{1$K9Qb7bedDJ<+fvBSI}_}*62bzn4Pd|F zwxyb%wJ(6PrJNpZ0mDk(8T8#dT~FRzQm7^+JgUg&G5C?0$h2eiC%c289L((BkvKxAdxj|OyvLm z&qUU)6~oA9ECxep6!?o*2GB6_di{XB6rJ-3wO9lbv7@J)Vn%Mh*^GbcQ)cqcJI(a= z?IzaPXcB#WCOI}{lChXcCX*&X_y6F+PY5Q-?m(Z`_H?nS> zk;0cyKJo2un|NJadgZ9uCLWhp0BXIE_TcA={+<4<^N>g}^|!y75u!r`@iC#~7WrfZ zt*9rdmbpZL?fRw*E}Rs7|i*(iScnWamO8I=)Cj92$FQl zwJ&s!APUczttpr*`TO94C4W+QwC$33jofydiS65G3lOmAF3g}tn3yo*Klp)(oOq(- z2^zf?G74NfTNIcp{O<@n{A;bJ@S>-kW~P4g8#@epBYXfxh#fp=hTroZGf1^uERiY=p_b{?$Yn~*R26w z3{!p@K*b;Xzy};P<>)>%x=mGAn?bsYKAS+b>xRdrGeM^Ce?lPQzyJMtE`9(epe*Pn z`eg#uZyH`^Ypi7I0bK7_`>%cc(Ko;OVJW`T1e1IBnn7zFf{!LR(bibW)C2f{A3Xpa z)*Zg)8ku2pivO@R!T2BlD7^zUfd3Tq%WCaC^$($QLuR_I@dPqoPgU%5T$!ePf42s z9|wcd8{kRM>xBU{^pAhx3$8=Iv*siwC(SgIDY2m;r$O>Yd*`*twNA4DH8C}1i#w-r z_5WkNy=L&FlVnke7fpd0pi;G-%K*=Y5j8;0SwC%BG8)wzbX|}ULSf@u-eM}wImhff z;RI98lI|LoZ+DSL#3GSYG)Ux)_;K#>5DP&XXzFUX*6QPrHwU=(cGd)pmX*0~{R$ctfMt^Jd~{H5NQyF_SY#MCo&x@*M>vuo8Vvxfzsd&wb$ zJ+E40cD?Ksru?Ln&G7Es+9WKv_{0DGuc>A!`o34Z!t7^atAcB-U~0R9Yu~?erKwoG z+H`&V<0`(s&KdR;yRPnR&m%h}r6Vc{1HgNB~W`If*r80S7fPQ$28w0HKs@pgD^Pjt_{VHJV$3A9u zzU*bDjQ1$RKxIV^iUA%hDK!!eD$hRKBrJmk-FKfpbjVa;_6p2cRf_Ojn^5JT8fv(w z{g|Nlhd)#U&H4Uu?ydLy^G!6jD4+%yhG)4kz;XJAS=W9XFYEadk%;TsKiSr1_7)YX z$hPolf3}a>wwsXK_4Ti7fd*dt!=L+{Dd!rf`6_%`@db$nl{5{tBzJKaIgwVH0rHAI zA`DR=R|Aa0^W7L=b1nwZ1s>y{{jA=ItHSIdTI_n|E2VZjg+D_42a7F^Ei(I#D>0Mp z?SaoL$XrGpwX=fe2yrtDZ&5>YP(e-*Wu2hgC@{sac_(EW#bh}eU zWzV6TsHOMQ#RAzwR4SrCE(VCfE8Q62bnQN5JL)$=S7^SObo0C3ZFa9%i2+KE6kc!& zUu+@2PrfnC977;Yf!0i#ENZ@GfXu?%2B^dUJ?vrBYjsmL(SQEaG|>zU=QcW+gjH?~ zaE=-v>*VkI-`DW(x;^dq{O8SXy9r1qyo&EM0JY-)V+27ZfI4np!-}3N3}1~ARQQbI zLv6|c19XjDnN@p7qobytoa!KE8_umuxYmsU&db&S+Wm{w)VL1G>hi(A|2-7}N*%&$ ztRh9cDOPc`(l@fd60s#!u_0-^ZWbgidCL`iiz17w$HtQLG~}~ zXTVDO`qguYUyDhqiH@z;U!T=yMcE-$&1Z!v>*4GTQ0$)paHdRPjD6@s>fCwXWd8#X zm^~zdBzJ^+7s4z2?tW=HUhzuPMc2&lC}5kmj4E{@LwY7USw8ERvL@ zZXa%UBfKqsh2#$_SDBVKywQIH=sW-!dL?ah4Te2JcpI9=TJDFQLsr+NzaM7*!~u*{ z&u8_~1&z+XD_G*j09UI4vKsxfH*{uMsI4SCdui`T?pz2DoGTSF?v5bQfB)Opra(oR zYHl_)2%JuMt^L)~_y5XQvc4Yun}P3r&m7>r8hFoE43&ieHoGyvRaqH;*)N{FrNZY{ zs9jyA9Eb0tU`g=OhkHeZFHUhB24J3`j${-=sTuw24l?h5fT`aa?w?wIr1fER$d}i_WTz+2$Xb=X0^nHCB5ftHR z=T&$B$qBUE=hf_EceiQUxXCn-=t^&(9>SPxc)<;ztA4-xT{VC={cLv9K1dC!;3j7k z-uW|-C{Rum==$U*eTf2ED@PfEmhK&btM~wzqDCr3S(*NR?v-MA#59v<)idTH&45G# zVL%hYg^mD|cif={@XYm%H{W9Rty(Q@yF1|>$XxwIZLNync{i|^rb?1L!u>AYV#IG`Hs~{;1oXdax8vdhHC5Rip9sqylIc^ z+E3)OYkw`Zvx$dB^6X(>qD}_}Xq3;eB0zKMwMRz?7pw{R?Qhipp0~A!e)J=vz$+xV zo3qv{)k-P~bg-~OMS-luSMwZVuY6ZXus$p(?mpb8lWZh+O1JwLArsgc=g->_LQhB!KUEV09D{>IhG=S zM-A=%#3!6vx^(Bxk?=F;R7FVYpe2rRcP5C6S_4o`q>l}QwTj5oFMcQl`t-}N| zkR$W#b=*h8>8A^(9zmJNk^ht5nW^#o%@N{5L#p-<{NyKkjlP+GoGoZgVp=}aDZJAF z9bBu62;kv#uuM;~IwnaC@_hy_5|IrS5(#FuU%9FEY0xoyYV}MPPGG zr|^pW9OJN1LxuO}zI7WK(Wr?gsFm8SV$WuRLz)oIb2|9)c^g3C17i&h>RMl;K>x3P zW%g?na9itD_zWPpqHTOy;gtq40Qtx$i$a1}d(ijc51VF^*tR(g(3YYd29GpAsrO65 zTn`3$9}OBA7Oj-FTxQQ_9li&LR%!_VEWm_S6boVk=?=~{}zR6 zPk=?4z8b(DiruD!8Wfs|DPJR=)$Ea=wo%5dB!R8m?`*;=?fjmg-NFb4 z0G|wCKMN?p2P<{3<(j^|WcGmx-zYhQEI467s2bAm`y8X0X~yi-epkXPZ4lNd;Br~` z`Pps^(WhO;+uHmOi`q46X)!gr@Y22TihJP|nSzyp81^8bCerPOHG!Yl321%&^#5bEEsp%6ksNKY&SoRQP!P%d)x4Q~h}G1B*s)Aej%r3~FS z$>2Q=_ex_i)2>WM*JJQDMiGN-<|Y}WYGI(wjrbjD=mZRqBnqsCP%j36kPy-nueOKl zw_(IPI#i@UqCkWVDY`Ppvv6;T-ylflFHbqw-93KSeq9buuF*pOpYEUQTCc)GGG%}` z$>1v?qXD#C*pib0bce7tF%;AxWA!zZ5MB&%*p8eMDo}i9yy47v8|7rl2!mIeU0Y*~KfSl6+tK27m?k9(W zGQi=^&KdEELJa55zn!;)7o^rVkX*JnclQKP`zM(EX(ggr;=32#VSw(Ek^%M)JQe0N zz)QS%P^3-~A7&9pP*Ff@W*ET%bSqkqkKxt_;gq-M!ZGcBZhZhFWZR0~c zM>o;muQwT3BJdxv!|dNZ`D$2uKfy;o#@M|!1+&+B zrJ35_Nb@oH?QaM6)G<8{UVE)+r>r`;1`ouy4R8m9SR}FxLW-UUc2D59o_=W*O+gd^ z`dDW-)!7+LKA`jH{e7m1&y~4*x^x+ZSDK~v)AqKXbB;dgJg{zGgh?$4{auuq2f`~c zkX=JxgAnJ-Af)JtV9y79-%A5%6d0ml4>DYvNF;)tCtxjI9o@nvt|O!HDO#lVGp8`h z?w&wqH1)d!dHP2?Jr29(cX@*v;C&$Z-g0LWsMudA_7Ok6I8vT<+e@#YTl0YczYMC2 z9m4gPpqXfN#J)O(2qoe-@VNTkk9;K1;oigb^)tfLM0Ii<9*VC`fGiNb5kiC);B zmT`*#nq-$PW@zD>!VFuFGItTA__7J*;H8(E4w63Feb2>L!kB>3-W6cY1On)Zb4Kvx zR`^eP9t8-)J-EG^>D9;`cLZVreQy=uFiXiMcqXANwup6i+rJl#`0PIFVW)2gO=8$T z@pTxucXZRHCxSH(0H7y=-3@#!g#9pp62%0vzpa``Fu>Mtj^Nte%v-+$N%-Oe1E%M) z%lsDK9t0|b&^;?w6hO#EPZZk(|M4RaP@>QeGFD$ppSxE#mIgWDg5VIy(ls^SG?C{Z z^Aw%r9Das<#~8rRAk@Ja0EBGxM6r7UU$uJzelQH3sjXMC%sR{xZ|!?)ZCUh(6;&p9 zq)eFnr<1%R0P*cE;9DSs36?|1Mo$#?djvgVFx5A25F^OGH@+)xs;(}uupCr#6J>nA zlcck&s7SN@AV`L1mV|GDP;Y5zA%tx7M6naWm-(3pD#Os7TENlBYdslpLs78|-Z;N6 zcu@);>*_Lnj6rnB2p{(cL5Z&k;Gr=RKsaI=SkaTTAW*s89rTly)U)tq>2(8UsG~{f zknFd#|YqSK$`bu5SlXw0J|C3Xg2|Vo1k6h z?d_R5!lf9XUUCCAenfaUQ(}DFS@40I!I|0Xak#qL^z)?<($;tC;Esm;V9|x3Q!E32 zQEDl80fe0BNk$N-B8X-n3N$+z7iToG$g}|?$U~9_d2T+tWu5JAFe8d-qR-^6U8bKN zOefb!-(Ey_CVC4M{(lc4hPn$Olny;{VmAYC4Wb!P6NF{nKEpJvOx83}$c+*a=uY*% z?|o)``*wXOqqBBqZ3_D-Vfr4zD-iJmyI2ZWvs z13@(f5ooa@K(m9Elo47;6q^`IZl>Tncrs@{PX!<6TSDUf{jS<;6&GAJB{D7{t93^B z&cD9bzE+YuY2zi_i|F|xygm9?1EIN?^*^mB@Zlh%K##Qj>BP6d2s2xyWg3^6v}VMR z{qya7M?g1g0S6da9R2<8%~VZ|n8NigC7n5pvA1!O$H+-7?`Qh@AQMP3U5n^5Sv}W4 z8?VCW6R}kCcfCFRn+ZKhixmYP2jL)c0YwtRCKWyntu~e!T3Pwij6lsa6D>pt*;6N` z=wg8B;7vD~UcS|&{{tT|10Vmm8T`y=%;2X!Wd@l4?`KC*FV7%6NJtN#b(YM}%PgHt zU7HS!)rrX{*nAaTiQ8lUIY$7nM+UD9Vq{RG010SX@muFYwoqdPcGAwg%D-tY+7Pe} zL0f4uS`oArbF|q=^uRbBg!B$f)X95c0u?;?Y~F*BKW+PGLr-SK>K5Jw2Bw2Bf!tKH zJK<-4YDQ@(fYfS%_qOQ-#C`}BdvP%h(tLgvLaZuU4xzcwlUZ?06E_DnI5Y^^3J+QB zriDc-J`sDXbDzgt=!qM&DR>2pX>$-nLfEn-{A|QhWB%V-!8XB*pxWvl#{EnPTbIJU5Z80s`hPN={v@!(55NEp6D))8T7tzO;ln(^ z!(I4%cuaCYOUvI%OjLMgmOVoa>ikT0=Ni% z2NPg27$N~lVLl1rQIg#!EwN)NxI^?IxD&xoRpBd+Ii|pU$oHcOaNjH_&xc##KIoc7 z;G|`Yn02m3QIb-4?a%?8Y3NeC6w5sci@1e~oS0)5PYe8lP=7TM_URgFx2{=J(2ejA z@)AAq8a09rAuErCBDfvC1OJ5%n4H&s0;0fC5bno8#Qq0-4Q`}{ui^e42_Z$+^4r2M z76T}Djv$26@Mw4;l)@$O2KWSg7w&<-!Y-(UM(CtqdMKr&fXe%x*=s;Yl&0PS?RcI(g>;g<9gccG#Srn*QqCjt;fWrMBVd$bz!zsEf zsGWkGyC$rEm*(gC3I($eS}f?v5)Vg~46wxi1LJf8^%YrP4gdfE07*qoM6N<$f+<9) AF8}}l literal 0 HcmV?d00001 diff --git a/dist/icons/color/browser/vivaldi.svg b/dist/icons/color/browser/vivaldi.svg new file mode 100644 index 000000000..2084b9758 --- /dev/null +++ b/dist/icons/color/browser/vivaldi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/webkit.png b/dist/icons/color/browser/webkit.png new file mode 100644 index 0000000000000000000000000000000000000000..cbc0f1b7b287ec6ee7a7bd845c3ac0403851c9b0 GIT binary patch literal 11500 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 SfS!H;0000 \ No newline at end of file diff --git a/dist/icons/color/browser/yandex.png b/dist/icons/color/browser/yandex.png new file mode 100644 index 0000000000000000000000000000000000000000..896456d205b1a84d5b510ed1c85115c7a2cdb0d2 GIT binary patch literal 10997 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 \ No newline at end of file diff --git a/src/icons/mono/LICENSE.md b/dist/icons/mono/LICENSE.md similarity index 100% rename from src/icons/mono/LICENSE.md rename to dist/icons/mono/LICENSE.md diff --git a/src/icons/mono/browser/alipay.svg b/dist/icons/mono/browser/alipay.svg similarity index 100% rename from src/icons/mono/browser/alipay.svg rename to dist/icons/mono/browser/alipay.svg diff --git a/src/icons/mono/browser/android-browser.svg b/dist/icons/mono/browser/android-browser.svg similarity index 100% rename from src/icons/mono/browser/android-browser.svg rename to dist/icons/mono/browser/android-browser.svg diff --git a/src/icons/mono/browser/avast-secure-browser.svg b/dist/icons/mono/browser/avast-secure-browser.svg similarity index 100% rename from src/icons/mono/browser/avast-secure-browser.svg rename to dist/icons/mono/browser/avast-secure-browser.svg diff --git a/src/icons/mono/browser/baidu-browser.svg b/dist/icons/mono/browser/baidu-browser.svg similarity index 100% rename from src/icons/mono/browser/baidu-browser.svg rename to dist/icons/mono/browser/baidu-browser.svg diff --git a/src/icons/mono/browser/brave.svg b/dist/icons/mono/browser/brave.svg similarity index 100% rename from src/icons/mono/browser/brave.svg rename to dist/icons/mono/browser/brave.svg diff --git a/src/icons/mono/browser/chrome-headless.svg b/dist/icons/mono/browser/chrome-headless.svg similarity index 100% rename from src/icons/mono/browser/chrome-headless.svg rename to dist/icons/mono/browser/chrome-headless.svg diff --git a/src/icons/mono/browser/chrome-webview.svg b/dist/icons/mono/browser/chrome-webview.svg similarity index 100% rename from src/icons/mono/browser/chrome-webview.svg rename to dist/icons/mono/browser/chrome-webview.svg diff --git a/src/icons/mono/browser/chrome.svg b/dist/icons/mono/browser/chrome.svg similarity index 100% rename from src/icons/mono/browser/chrome.svg rename to dist/icons/mono/browser/chrome.svg diff --git a/src/icons/mono/browser/chromium.svg b/dist/icons/mono/browser/chromium.svg similarity index 100% rename from src/icons/mono/browser/chromium.svg rename to dist/icons/mono/browser/chromium.svg diff --git a/src/icons/mono/browser/duckduckgo.svg b/dist/icons/mono/browser/duckduckgo.svg similarity index 100% rename from src/icons/mono/browser/duckduckgo.svg rename to dist/icons/mono/browser/duckduckgo.svg diff --git a/src/icons/mono/browser/edge.svg b/dist/icons/mono/browser/edge.svg similarity index 100% rename from src/icons/mono/browser/edge.svg rename to dist/icons/mono/browser/edge.svg diff --git a/src/icons/mono/browser/electron.svg b/dist/icons/mono/browser/electron.svg similarity index 100% rename from src/icons/mono/browser/electron.svg rename to dist/icons/mono/browser/electron.svg diff --git a/src/icons/mono/browser/facebook.svg b/dist/icons/mono/browser/facebook.svg similarity index 100% rename from src/icons/mono/browser/facebook.svg rename to dist/icons/mono/browser/facebook.svg diff --git a/src/icons/mono/browser/firefox-focus.svg b/dist/icons/mono/browser/firefox-focus.svg similarity index 100% rename from src/icons/mono/browser/firefox-focus.svg rename to dist/icons/mono/browser/firefox-focus.svg diff --git a/src/icons/mono/browser/firefox-reality.svg b/dist/icons/mono/browser/firefox-reality.svg similarity index 100% rename from src/icons/mono/browser/firefox-reality.svg rename to dist/icons/mono/browser/firefox-reality.svg diff --git a/src/icons/mono/browser/firefox.svg b/dist/icons/mono/browser/firefox.svg similarity index 100% rename from src/icons/mono/browser/firefox.svg rename to dist/icons/mono/browser/firefox.svg diff --git a/src/icons/mono/browser/gsa.svg b/dist/icons/mono/browser/gsa.svg similarity index 100% rename from src/icons/mono/browser/gsa.svg rename to dist/icons/mono/browser/gsa.svg diff --git a/src/icons/mono/browser/huawei-browser.svg b/dist/icons/mono/browser/huawei-browser.svg similarity index 100% rename from src/icons/mono/browser/huawei-browser.svg rename to dist/icons/mono/browser/huawei-browser.svg diff --git a/src/icons/mono/browser/icecat.svg b/dist/icons/mono/browser/icecat.svg similarity index 100% rename from src/icons/mono/browser/icecat.svg rename to dist/icons/mono/browser/icecat.svg diff --git a/src/icons/mono/browser/ie.svg b/dist/icons/mono/browser/ie.svg similarity index 100% rename from src/icons/mono/browser/ie.svg rename to dist/icons/mono/browser/ie.svg diff --git a/src/icons/mono/browser/instagram.svg b/dist/icons/mono/browser/instagram.svg similarity index 100% rename from src/icons/mono/browser/instagram.svg rename to dist/icons/mono/browser/instagram.svg diff --git a/src/icons/mono/browser/jasmine.svg b/dist/icons/mono/browser/jasmine.svg similarity index 100% rename from src/icons/mono/browser/jasmine.svg rename to dist/icons/mono/browser/jasmine.svg diff --git a/src/icons/mono/browser/kakaotalk.svg b/dist/icons/mono/browser/kakaotalk.svg similarity index 100% rename from src/icons/mono/browser/kakaotalk.svg rename to dist/icons/mono/browser/kakaotalk.svg diff --git a/src/icons/mono/browser/klarna.svg b/dist/icons/mono/browser/klarna.svg similarity index 100% rename from src/icons/mono/browser/klarna.svg rename to dist/icons/mono/browser/klarna.svg diff --git a/src/icons/mono/browser/line.svg b/dist/icons/mono/browser/line.svg similarity index 100% rename from src/icons/mono/browser/line.svg rename to dist/icons/mono/browser/line.svg diff --git a/src/icons/mono/browser/linkedin.svg b/dist/icons/mono/browser/linkedin.svg similarity index 100% rename from src/icons/mono/browser/linkedin.svg rename to dist/icons/mono/browser/linkedin.svg diff --git a/src/icons/mono/browser/miui-browser.svg b/dist/icons/mono/browser/miui-browser.svg similarity index 100% rename from src/icons/mono/browser/miui-browser.svg rename to dist/icons/mono/browser/miui-browser.svg diff --git a/src/icons/mono/browser/mobile-chrome.svg b/dist/icons/mono/browser/mobile-chrome.svg similarity index 100% rename from src/icons/mono/browser/mobile-chrome.svg rename to dist/icons/mono/browser/mobile-chrome.svg diff --git a/src/icons/mono/browser/mobile-firefox.svg b/dist/icons/mono/browser/mobile-firefox.svg similarity index 100% rename from src/icons/mono/browser/mobile-firefox.svg rename to dist/icons/mono/browser/mobile-firefox.svg diff --git a/src/icons/mono/browser/mobile-safari.svg b/dist/icons/mono/browser/mobile-safari.svg similarity index 100% rename from src/icons/mono/browser/mobile-safari.svg rename to dist/icons/mono/browser/mobile-safari.svg diff --git a/src/icons/mono/browser/mozilla.svg b/dist/icons/mono/browser/mozilla.svg similarity index 100% rename from src/icons/mono/browser/mozilla.svg rename to dist/icons/mono/browser/mozilla.svg diff --git a/src/icons/mono/browser/naver.svg b/dist/icons/mono/browser/naver.svg similarity index 100% rename from src/icons/mono/browser/naver.svg rename to dist/icons/mono/browser/naver.svg diff --git a/src/icons/mono/browser/nokia-browser.svg b/dist/icons/mono/browser/nokia-browser.svg similarity index 100% rename from src/icons/mono/browser/nokia-browser.svg rename to dist/icons/mono/browser/nokia-browser.svg diff --git a/src/icons/mono/browser/oculus-browser.svg b/dist/icons/mono/browser/oculus-browser.svg similarity index 100% rename from src/icons/mono/browser/oculus-browser.svg rename to dist/icons/mono/browser/oculus-browser.svg diff --git a/src/icons/mono/browser/opera-coast.svg b/dist/icons/mono/browser/opera-coast.svg similarity index 100% rename from src/icons/mono/browser/opera-coast.svg rename to dist/icons/mono/browser/opera-coast.svg diff --git a/src/icons/mono/browser/opera-gx.svg b/dist/icons/mono/browser/opera-gx.svg similarity index 100% rename from src/icons/mono/browser/opera-gx.svg rename to dist/icons/mono/browser/opera-gx.svg diff --git a/src/icons/mono/browser/opera-mini.svg b/dist/icons/mono/browser/opera-mini.svg similarity index 100% rename from src/icons/mono/browser/opera-mini.svg rename to dist/icons/mono/browser/opera-mini.svg diff --git a/src/icons/mono/browser/opera-mobi.svg b/dist/icons/mono/browser/opera-mobi.svg similarity index 100% rename from src/icons/mono/browser/opera-mobi.svg rename to dist/icons/mono/browser/opera-mobi.svg diff --git a/src/icons/mono/browser/opera-tablet.svg b/dist/icons/mono/browser/opera-tablet.svg similarity index 100% rename from src/icons/mono/browser/opera-tablet.svg rename to dist/icons/mono/browser/opera-tablet.svg diff --git a/src/icons/mono/browser/opera-touch.svg b/dist/icons/mono/browser/opera-touch.svg similarity index 100% rename from src/icons/mono/browser/opera-touch.svg rename to dist/icons/mono/browser/opera-touch.svg diff --git a/src/icons/mono/browser/opera.svg b/dist/icons/mono/browser/opera.svg similarity index 100% rename from src/icons/mono/browser/opera.svg rename to dist/icons/mono/browser/opera.svg diff --git a/src/icons/mono/browser/qqbrowser.svg b/dist/icons/mono/browser/qqbrowser.svg similarity index 100% rename from src/icons/mono/browser/qqbrowser.svg rename to dist/icons/mono/browser/qqbrowser.svg diff --git a/src/icons/mono/browser/qqbrowserlite.svg b/dist/icons/mono/browser/qqbrowserlite.svg similarity index 100% rename from src/icons/mono/browser/qqbrowserlite.svg rename to dist/icons/mono/browser/qqbrowserlite.svg diff --git a/src/icons/mono/browser/safari.svg b/dist/icons/mono/browser/safari.svg similarity index 100% rename from src/icons/mono/browser/safari.svg rename to dist/icons/mono/browser/safari.svg diff --git a/src/icons/mono/browser/sailfish-browser.svg b/dist/icons/mono/browser/sailfish-browser.svg similarity index 100% rename from src/icons/mono/browser/sailfish-browser.svg rename to dist/icons/mono/browser/sailfish-browser.svg diff --git a/src/icons/mono/browser/samsung-internet.svg b/dist/icons/mono/browser/samsung-internet.svg similarity index 100% rename from src/icons/mono/browser/samsung-internet.svg rename to dist/icons/mono/browser/samsung-internet.svg diff --git a/src/icons/mono/browser/smart-lenovo-browser.svg b/dist/icons/mono/browser/smart-lenovo-browser.svg similarity index 100% rename from src/icons/mono/browser/smart-lenovo-browser.svg rename to dist/icons/mono/browser/smart-lenovo-browser.svg diff --git a/src/icons/mono/browser/snapchat.svg b/dist/icons/mono/browser/snapchat.svg similarity index 100% rename from src/icons/mono/browser/snapchat.svg rename to dist/icons/mono/browser/snapchat.svg diff --git a/src/icons/mono/browser/sogou-explorer.svg b/dist/icons/mono/browser/sogou-explorer.svg similarity index 100% rename from src/icons/mono/browser/sogou-explorer.svg rename to dist/icons/mono/browser/sogou-explorer.svg diff --git a/src/icons/mono/browser/sogou-mobile.svg b/dist/icons/mono/browser/sogou-mobile.svg similarity index 100% rename from src/icons/mono/browser/sogou-mobile.svg rename to dist/icons/mono/browser/sogou-mobile.svg diff --git a/src/icons/mono/browser/tesla.svg b/dist/icons/mono/browser/tesla.svg similarity index 100% rename from src/icons/mono/browser/tesla.svg rename to dist/icons/mono/browser/tesla.svg diff --git a/src/icons/mono/browser/tiktok.svg b/dist/icons/mono/browser/tiktok.svg similarity index 100% rename from src/icons/mono/browser/tiktok.svg rename to dist/icons/mono/browser/tiktok.svg diff --git a/src/icons/mono/browser/twitter.svg b/dist/icons/mono/browser/twitter.svg similarity index 100% rename from src/icons/mono/browser/twitter.svg rename to dist/icons/mono/browser/twitter.svg diff --git a/src/icons/mono/browser/vivaldi.svg b/dist/icons/mono/browser/vivaldi.svg similarity index 100% rename from src/icons/mono/browser/vivaldi.svg rename to dist/icons/mono/browser/vivaldi.svg diff --git a/src/icons/mono/browser/vivo-browser.svg b/dist/icons/mono/browser/vivo-browser.svg similarity index 100% rename from src/icons/mono/browser/vivo-browser.svg rename to dist/icons/mono/browser/vivo-browser.svg diff --git a/src/icons/mono/browser/wechat.svg b/dist/icons/mono/browser/wechat.svg similarity index 100% rename from src/icons/mono/browser/wechat.svg rename to dist/icons/mono/browser/wechat.svg diff --git a/src/icons/mono/browser/weibo.svg b/dist/icons/mono/browser/weibo.svg similarity index 100% rename from src/icons/mono/browser/weibo.svg rename to dist/icons/mono/browser/weibo.svg diff --git a/src/icons/mono/browser/yandex.svg b/dist/icons/mono/browser/yandex.svg similarity index 100% rename from src/icons/mono/browser/yandex.svg rename to dist/icons/mono/browser/yandex.svg diff --git a/src/icons/mono/device/acer.svg b/dist/icons/mono/device/acer.svg similarity index 100% rename from src/icons/mono/device/acer.svg rename to dist/icons/mono/device/acer.svg diff --git a/src/icons/mono/device/amazon.svg b/dist/icons/mono/device/amazon.svg similarity index 100% rename from src/icons/mono/device/amazon.svg rename to dist/icons/mono/device/amazon.svg diff --git a/src/icons/mono/device/apple.svg b/dist/icons/mono/device/apple.svg similarity index 100% rename from src/icons/mono/device/apple.svg rename to dist/icons/mono/device/apple.svg diff --git a/src/icons/mono/device/asus.svg b/dist/icons/mono/device/asus.svg similarity index 100% rename from src/icons/mono/device/asus.svg rename to dist/icons/mono/device/asus.svg diff --git a/src/icons/mono/device/att.svg b/dist/icons/mono/device/att.svg similarity index 100% rename from src/icons/mono/device/att.svg rename to dist/icons/mono/device/att.svg diff --git a/src/icons/mono/device/blackberry.svg b/dist/icons/mono/device/blackberry.svg similarity index 100% rename from src/icons/mono/device/blackberry.svg rename to dist/icons/mono/device/blackberry.svg diff --git a/src/icons/mono/device/dell.svg b/dist/icons/mono/device/dell.svg similarity index 100% rename from src/icons/mono/device/dell.svg rename to dist/icons/mono/device/dell.svg diff --git a/src/icons/mono/device/facebook.svg b/dist/icons/mono/device/facebook.svg similarity index 100% rename from src/icons/mono/device/facebook.svg rename to dist/icons/mono/device/facebook.svg diff --git a/src/icons/mono/device/fairphone.svg b/dist/icons/mono/device/fairphone.svg similarity index 100% rename from src/icons/mono/device/fairphone.svg rename to dist/icons/mono/device/fairphone.svg diff --git a/src/icons/mono/device/google.svg b/dist/icons/mono/device/google.svg similarity index 100% rename from src/icons/mono/device/google.svg rename to dist/icons/mono/device/google.svg diff --git a/src/icons/mono/device/hp.svg b/dist/icons/mono/device/hp.svg similarity index 100% rename from src/icons/mono/device/hp.svg rename to dist/icons/mono/device/hp.svg diff --git a/src/icons/mono/device/huawei.svg b/dist/icons/mono/device/huawei.svg similarity index 100% rename from src/icons/mono/device/huawei.svg rename to dist/icons/mono/device/huawei.svg diff --git a/src/icons/mono/device/lenovo.svg b/dist/icons/mono/device/lenovo.svg similarity index 100% rename from src/icons/mono/device/lenovo.svg rename to dist/icons/mono/device/lenovo.svg diff --git a/src/icons/mono/device/lg.svg b/dist/icons/mono/device/lg.svg similarity index 100% rename from src/icons/mono/device/lg.svg rename to dist/icons/mono/device/lg.svg diff --git a/src/icons/mono/device/meizu.svg b/dist/icons/mono/device/meizu.svg similarity index 100% rename from src/icons/mono/device/meizu.svg rename to dist/icons/mono/device/meizu.svg diff --git a/src/icons/mono/device/microsoft.svg b/dist/icons/mono/device/microsoft.svg similarity index 100% rename from src/icons/mono/device/microsoft.svg rename to dist/icons/mono/device/microsoft.svg diff --git a/src/icons/mono/device/motorola.svg b/dist/icons/mono/device/motorola.svg similarity index 100% rename from src/icons/mono/device/motorola.svg rename to dist/icons/mono/device/motorola.svg diff --git a/src/icons/mono/device/nintendo.svg b/dist/icons/mono/device/nintendo.svg similarity index 100% rename from src/icons/mono/device/nintendo.svg rename to dist/icons/mono/device/nintendo.svg diff --git a/src/icons/mono/device/nokia.svg b/dist/icons/mono/device/nokia.svg similarity index 100% rename from src/icons/mono/device/nokia.svg rename to dist/icons/mono/device/nokia.svg diff --git a/src/icons/mono/device/nvidia.svg b/dist/icons/mono/device/nvidia.svg similarity index 100% rename from src/icons/mono/device/nvidia.svg rename to dist/icons/mono/device/nvidia.svg diff --git a/src/icons/mono/device/oneplus.svg b/dist/icons/mono/device/oneplus.svg similarity index 100% rename from src/icons/mono/device/oneplus.svg rename to dist/icons/mono/device/oneplus.svg diff --git a/src/icons/mono/device/oppo.svg b/dist/icons/mono/device/oppo.svg similarity index 100% rename from src/icons/mono/device/oppo.svg rename to dist/icons/mono/device/oppo.svg diff --git a/src/icons/mono/device/panasonic.svg b/dist/icons/mono/device/panasonic.svg similarity index 100% rename from src/icons/mono/device/panasonic.svg rename to dist/icons/mono/device/panasonic.svg diff --git a/src/icons/mono/device/roku.svg b/dist/icons/mono/device/roku.svg similarity index 100% rename from src/icons/mono/device/roku.svg rename to dist/icons/mono/device/roku.svg diff --git a/src/icons/mono/device/samsung.svg b/dist/icons/mono/device/samsung.svg similarity index 100% rename from src/icons/mono/device/samsung.svg rename to dist/icons/mono/device/samsung.svg diff --git a/src/icons/mono/device/siemens.svg b/dist/icons/mono/device/siemens.svg similarity index 100% rename from src/icons/mono/device/siemens.svg rename to dist/icons/mono/device/siemens.svg diff --git a/src/icons/mono/device/sony.svg b/dist/icons/mono/device/sony.svg similarity index 100% rename from src/icons/mono/device/sony.svg rename to dist/icons/mono/device/sony.svg diff --git a/src/icons/mono/device/tesla.svg b/dist/icons/mono/device/tesla.svg similarity index 100% rename from src/icons/mono/device/tesla.svg rename to dist/icons/mono/device/tesla.svg diff --git a/src/icons/mono/device/vivo.svg b/dist/icons/mono/device/vivo.svg similarity index 100% rename from src/icons/mono/device/vivo.svg rename to dist/icons/mono/device/vivo.svg diff --git a/src/icons/mono/device/vodafone.svg b/dist/icons/mono/device/vodafone.svg similarity index 100% rename from src/icons/mono/device/vodafone.svg rename to dist/icons/mono/device/vodafone.svg diff --git a/src/icons/mono/device/xiaomi.svg b/dist/icons/mono/device/xiaomi.svg similarity index 100% rename from src/icons/mono/device/xiaomi.svg rename to dist/icons/mono/device/xiaomi.svg diff --git a/src/icons/mono/device/zebra.svg b/dist/icons/mono/device/zebra.svg similarity index 100% rename from src/icons/mono/device/zebra.svg rename to dist/icons/mono/device/zebra.svg diff --git a/src/icons/mono/os/android.svg b/dist/icons/mono/os/android.svg similarity index 100% rename from src/icons/mono/os/android.svg rename to dist/icons/mono/os/android.svg diff --git a/src/icons/mono/os/arch.svg b/dist/icons/mono/os/arch.svg similarity index 100% rename from src/icons/mono/os/arch.svg rename to dist/icons/mono/os/arch.svg diff --git a/src/icons/mono/os/blackberry.svg b/dist/icons/mono/os/blackberry.svg similarity index 100% rename from src/icons/mono/os/blackberry.svg rename to dist/icons/mono/os/blackberry.svg diff --git a/src/icons/mono/os/centos.svg b/dist/icons/mono/os/centos.svg similarity index 100% rename from src/icons/mono/os/centos.svg rename to dist/icons/mono/os/centos.svg diff --git a/src/icons/mono/os/chrome_os.svg b/dist/icons/mono/os/chrome-os.svg similarity index 100% rename from src/icons/mono/os/chrome_os.svg rename to dist/icons/mono/os/chrome-os.svg diff --git a/src/icons/mono/os/chromecast.svg b/dist/icons/mono/os/chromecast.svg similarity index 100% rename from src/icons/mono/os/chromecast.svg rename to dist/icons/mono/os/chromecast.svg diff --git a/src/icons/mono/os/debian.svg b/dist/icons/mono/os/debian.svg similarity index 100% rename from src/icons/mono/os/debian.svg rename to dist/icons/mono/os/debian.svg diff --git a/src/icons/mono/os/deepin.svg b/dist/icons/mono/os/deepin.svg similarity index 100% rename from src/icons/mono/os/deepin.svg rename to dist/icons/mono/os/deepin.svg diff --git a/src/icons/mono/os/elementary_os.svg b/dist/icons/mono/os/elementary-os.svg similarity index 100% rename from src/icons/mono/os/elementary_os.svg rename to dist/icons/mono/os/elementary-os.svg diff --git a/src/icons/mono/os/fedora.svg b/dist/icons/mono/os/fedora.svg similarity index 100% rename from src/icons/mono/os/fedora.svg rename to dist/icons/mono/os/fedora.svg diff --git a/src/icons/mono/os/firefox.svg b/dist/icons/mono/os/firefox.svg similarity index 100% rename from src/icons/mono/os/firefox.svg rename to dist/icons/mono/os/firefox.svg diff --git a/src/icons/mono/os/freebsd.svg b/dist/icons/mono/os/freebsd.svg similarity index 100% rename from src/icons/mono/os/freebsd.svg rename to dist/icons/mono/os/freebsd.svg diff --git a/src/icons/mono/os/gentoo.svg b/dist/icons/mono/os/gentoo.svg similarity index 100% rename from src/icons/mono/os/gentoo.svg rename to dist/icons/mono/os/gentoo.svg diff --git a/src/icons/mono/os/gnu.svg b/dist/icons/mono/os/gnu.svg similarity index 100% rename from src/icons/mono/os/gnu.svg rename to dist/icons/mono/os/gnu.svg diff --git a/src/icons/mono/os/harmonyos.svg b/dist/icons/mono/os/harmonyos.svg similarity index 100% rename from src/icons/mono/os/harmonyos.svg rename to dist/icons/mono/os/harmonyos.svg diff --git a/src/icons/mono/os/ios.svg b/dist/icons/mono/os/ios.svg similarity index 100% rename from src/icons/mono/os/ios.svg rename to dist/icons/mono/os/ios.svg diff --git a/src/icons/mono/os/kaios.svg b/dist/icons/mono/os/kaios.svg similarity index 100% rename from src/icons/mono/os/kaios.svg rename to dist/icons/mono/os/kaios.svg diff --git a/src/icons/mono/os/linux.svg b/dist/icons/mono/os/linux.svg similarity index 100% rename from src/icons/mono/os/linux.svg rename to dist/icons/mono/os/linux.svg diff --git a/src/icons/mono/os/macos.svg b/dist/icons/mono/os/macos.svg similarity index 100% rename from src/icons/mono/os/macos.svg rename to dist/icons/mono/os/macos.svg diff --git a/src/icons/mono/os/manjaro.svg b/dist/icons/mono/os/manjaro.svg similarity index 100% rename from src/icons/mono/os/manjaro.svg rename to dist/icons/mono/os/manjaro.svg diff --git a/src/icons/mono/os/mint.svg b/dist/icons/mono/os/mint.svg similarity index 100% rename from src/icons/mono/os/mint.svg rename to dist/icons/mono/os/mint.svg diff --git a/src/icons/mono/os/netbsd.svg b/dist/icons/mono/os/netbsd.svg similarity index 100% rename from src/icons/mono/os/netbsd.svg rename to dist/icons/mono/os/netbsd.svg diff --git a/src/icons/mono/os/nintendo.svg b/dist/icons/mono/os/nintendo.svg similarity index 100% rename from src/icons/mono/os/nintendo.svg rename to dist/icons/mono/os/nintendo.svg diff --git a/src/icons/mono/os/openbsd.svg b/dist/icons/mono/os/openbsd.svg similarity index 100% rename from src/icons/mono/os/openbsd.svg rename to dist/icons/mono/os/openbsd.svg diff --git a/src/icons/mono/os/playstation.svg b/dist/icons/mono/os/playstation.svg similarity index 100% rename from src/icons/mono/os/playstation.svg rename to dist/icons/mono/os/playstation.svg diff --git a/src/icons/mono/os/raspbian.svg b/dist/icons/mono/os/raspbian.svg similarity index 100% rename from src/icons/mono/os/raspbian.svg rename to dist/icons/mono/os/raspbian.svg diff --git a/src/icons/mono/os/redhat.svg b/dist/icons/mono/os/redhat.svg similarity index 100% rename from src/icons/mono/os/redhat.svg rename to dist/icons/mono/os/redhat.svg diff --git a/src/icons/mono/os/sailfish.svg b/dist/icons/mono/os/sailfish.svg similarity index 100% rename from src/icons/mono/os/sailfish.svg rename to dist/icons/mono/os/sailfish.svg diff --git a/src/icons/mono/os/slackware.svg b/dist/icons/mono/os/slackware.svg similarity index 100% rename from src/icons/mono/os/slackware.svg rename to dist/icons/mono/os/slackware.svg diff --git a/src/icons/mono/os/suse.svg b/dist/icons/mono/os/suse.svg similarity index 100% rename from src/icons/mono/os/suse.svg rename to dist/icons/mono/os/suse.svg diff --git a/src/icons/mono/os/ubuntu.svg b/dist/icons/mono/os/ubuntu.svg similarity index 100% rename from src/icons/mono/os/ubuntu.svg rename to dist/icons/mono/os/ubuntu.svg diff --git a/src/icons/mono/os/windows.svg b/dist/icons/mono/os/windows.svg similarity index 100% rename from src/icons/mono/os/windows.svg rename to dist/icons/mono/os/windows.svg diff --git a/src/icons/mono/os/xbox.svg b/dist/icons/mono/os/xbox.svg similarity index 100% rename from src/icons/mono/os/xbox.svg rename to dist/icons/mono/os/xbox.svg diff --git a/src/icons/ua-parser-icons.js b/src/icons/ua-parser-icons.js deleted file mode 100644 index e69de29bb..000000000 From aad163ffc530474aa748eade44f587a39ac97b69 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 29 Oct 2024 21:24:16 +0700 Subject: [PATCH 240/388] Change `withFeatureCheck()` return value to also include `PromiseLike` as an anticipation --- src/main/ua-parser.d.ts | 2 +- test/dts-test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index b83783180..03be97a52 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -8,7 +8,7 @@ declare namespace UAParser { is(val: string): boolean; toString(): string; withClientHints(): PromiseLike | T; - withFeatureCheck(): T; + withFeatureCheck(): PromiseLike | T; } interface IBrowser extends IData { diff --git a/test/dts-test.ts b/test/dts-test.ts index edf1d47d6..0f05a8aa2 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -32,8 +32,8 @@ expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | ' expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); -expectType((browser.withClientHints()).withFeatureCheck()); -expectType((browser.withClientHints()).withFeatureCheck().is('')); +expectType>((browser.withClientHints()).withFeatureCheck()); +expectType(((browser.withClientHints()).withFeatureCheck()).is('')); expectType(parser.getCPU()); expectType(parser.getDevice()); From 1e0175c001b60a0d809a6e4388954492a9cacc19 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 29 Oct 2024 22:29:51 +0700 Subject: [PATCH 241/388] Update version to `2.0.0-rc.1` --- CHANGELOG.md | 20 +++ README.md | 83 ++--------- dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- package-lock.json | 4 +- package.json | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 15 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 177 +++++++++++++----------- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 51 ++++++- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 76 +++++++--- 14 files changed, 255 insertions(+), 191 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80dfe6423..63d6bf0f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # UAParser.js Changelog +## Version 2.0.0-rc.1 + +- Fix Python Request mistakenly identified as Meta Quest +- Add new browser: Helio +- Add new device: itel, Nothing, Pico, TCL +- Add new engine: ArkWeb +- Add new OS: OpenHarmony, Pico +- Improve browser detection: Quark +- Improve device detection: Xiaomi, Amazon Echo Show, Google Chromecast, Samsung Galaxy Watch +- `ua-parser-js/helpers` submodule: + - Add new method: + - `getDeviceVendor()` to guess for a device vendor based on its model name + - `isElectron()` to check if current window is running inside Electron + - `isFromEU()` to check if current window is from an EU (European Union) country + - `isStandalonePWA()` to check if current window is a standalone PWA + - Rename `isChromiumBased()` to `isChromeFamily()` + - Update `isAppleSilicon()` to also checks for WebGL renderer info +- `ua-parser-js/extensions` submodule: + - Restore `bots` as a compilation of all these browser types: `clis`, `crawlers`, `fetchers`, and `modules` + ## Version 2.0.0-beta.3 - Breaking: diff --git a/README.md b/README.md index 57e0473bf..701add0cd 100644 --- a/README.md +++ b/README.md @@ -19,81 +19,14 @@ The most comprehensive, compact, & up-to-date isomorphic JavaScript library to d user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser (client-side) or node.js (server-side). -# Overview - -```js -import { UAParser } from 'ua-parser-js'; - -// 1. Problem: -// Imagine getting this wild user-agent string from a visitor: -const ua = `Mozilla/5.0 (Linux; Android 10; STK-LX1 -Build/HONORSTK-LX1; wv) AppleWebKit/537.36 (KHTML, -like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile -Safari/537.36 musical_ly_2022803040 JsSdk/1.0 -NetType/WIFI Channel/huaweiadsglobal_int -AppName/musical_ly app_version/28.3.4 ByteLocale/en -ByteFullLocale/en Region/IQ Spark/1.2.7-alpha.8 -AppVersion/28.3.4 PIA/1.5.11 BytedanceWebview/d8a21c6`; -// Note: this is a real user-agent (what???) - -// 2. Solution: -// Just pass the complex user-agent string to `UAParser` -const parser = new UAParser(ua); - -// 3. Result: -// And voila! -console.log(parser.getBrowser()); -// { name : "TikTok", version : "28.3.4", major : "28", type: undefined } - -console.log(parser.getCPU()); -// { architecture : undefined } - -console.log(parser.getEngine()); -// { name : "Blink", version : "110.0.5481.153" } - -console.log(parser.getDevice()); -// { type : "mobile", vendor : "Huawei", model : "STK-LX1" } - -console.log(parser.getOS()); -// { name : "Android", version : "10" } - -console.log(parser.getResult()); -/* -{ - ua: "Mozilla/5.0 (Linux; Android 10; STK-LX1 Build/HONORSTK-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile Safari/537.36 musical_ly_2022803040 JsSdk/1.0 NetType/WIFI Channel/huaweiadsglobal_int AppName/musical_ly app_version/28.3.4 ByteLocale/en ByteFullLocale/en Region/IQ Spark/1.2.7-alpha.8 AppVersion/28.3.4 PIA/1.5.11 BytedanceWebview/d8a21c6", - browser: { - name: "TikTok", - version: "28.3.4", - major: "28" - }, - cpu: {}, - device: { - type: "mobile", - model: "STK-LX1", - vendor: "Huawei" - }, - engine: { - name: "Blink", - version: "110.0.5481.153" - }, - os: { - name: "Android", - version: "10" - } -} -*/ - -// 4. Conclusion: -// The visitor is browsing from a TikTok app using an Android-powered Huawei device -// Phew! Thanks, UAParser.js! -``` +# Demo * Live demo: https://uaparser.dev # Documentation - * v1.0: https://github.com/faisalman/ua-parser-js/tree/1.0.38#documentation - * v2.0: https://docs.uaparser.dev + * `version 1.x` : https://github.com/faisalman/ua-parser-js/tree/1.0.x#documentation + * `version 2.x` : https://docs.uaparser.dev Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to see what's new & breaking. @@ -234,7 +167,7 @@ see what's new & breaking. ✅ ✅ ✅ - ⚠️ + ⛔️ ✅ @@ -257,9 +190,9 @@ see what's new & breaking. Price FREE (License) FREE (License) - $12 (License) - $25 (License) - $500 (License) + $14 (License) + $29 (License) + $588 (License) @@ -287,3 +220,5 @@ Made with [contributors-img](https://contrib.rocks). + +Support the open-source versions of UAParser.js on [OpenCollective](https://opencollective.com/ua-parser-js) or [GitHub Sponsors](https://github.com/sponsors/faisalman). diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 8819c5a6f..4187195e4 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta.3 +/* UAParser.js v2.0.0-rc.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-beta.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-rc.1",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 5b538966c..e22b892ce 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-beta.3 +/* UAParser.js v2.0.0-rc.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -!function(i,c){"use strict";function e(i){for(var e={},t=0;t_?Si(i,_):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-beta.3",Ui.BROWSER=e([f,v,p,m]),Ui.CPU=e([x]),Ui.DEVICE=e([h,g,m,k,y,t,r,o,a]),Ui.ENGINE=Ui.OS=e([f,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):ui&&(i.UAParser=Ui);var Hi,Ei=ui&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(Hi=new Ui,Ei.ua=Hi.getResult(),Ei.ua.get=function(){return Hi.getUA()},Ei.ua.set=function(i){Hi.setUA(i);var e,t=Hi.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,c){"use strict";function e(i){for(var e={},t=0;tS?Ti(i,S):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-rc.1",Ui.BROWSER=e([m,v,h,f]),Ui.CPU=e([k]),Ui.DEVICE=e([p,g,f,x,y,t,r,o,d]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===l&&define.amd?define(function(){return Ui}):ui&&(i.UAParser=Ui);var ji,Ei=ui&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(ji=new Ui,Ei.ua=ji.getResult(),Ei.ua.get=function(){return ji.getUA()},Ei.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c1e325174..1c4d590fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-beta.3", + "version": "2.0.0-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-beta.3", + "version": "2.0.0-rc.1", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index 6a7d31ac2..7674e9273 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-beta.3", + "version": "2.0.0-rc.1", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index e80a0d57a..4fc06b4d0 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta.3 +/* Enums for UAParser.js v2.0.0-rc.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 539cf3059..52df47f6e 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-beta.3 +/* Enums for UAParser.js v2.0.0-rc.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -12,8 +12,8 @@ /*jshint esversion: 6 */ const Browser = Object.freeze({ - _2345_EXPLORER: '2345Explorer', - _360: '360 Browser', + '2345_EXPLORER': '2345Explorer', + '360': '360 Browser', ALIPAY: 'Alipay', AMAYA: 'Amaya', ANDROID: 'Android Browser', @@ -56,6 +56,7 @@ const Browser = Object.freeze({ FLOW: 'Flow', GO: 'GoBrowser', GOOGLE_SEARCH: 'GSA', + HELIO: 'Helio', HEYTAP: 'HeyTap', HUAWEI: 'Huawei Browser', ICAB: 'iCab', @@ -161,6 +162,7 @@ const BrowserType = Object.freeze({ }); const CPU = Object.freeze({ + '68K': '68k', ARM : 'arm', ARM_64: 'arm64', ARM_HF: 'armhf', @@ -171,7 +173,6 @@ const CPU = Object.freeze({ IRIX_64: 'irix64', MIPS: 'mips', MIPS_64: 'mips64', - M68K: '68k', PA_RISC: 'pa-risc', PPC: 'ppc', SPARC: 'sparc', @@ -212,6 +213,7 @@ const Vendor = Object.freeze({ HTC: 'HTC', HUAWEI: 'Huawei', INFINIX: 'Infinix', + ITEL: 'itel', JOLLA: 'Jolla', KOBO: 'Kobo', LENOVO: 'Lenovo', @@ -222,6 +224,7 @@ const Vendor = Object.freeze({ NEXIAN: 'Nexian', NINTENDO: 'Nintendo', NOKIA: 'Nokia', + NOTHING: 'Nothing', NVIDIA: 'Nvidia', ONEPLUS: 'OnePlus', OPPO: 'OPPO', @@ -239,6 +242,7 @@ const Vendor = Object.freeze({ SIEMENS: 'Siemens', SONY: 'Sony', SPRINT: 'Sprint', + TCL: 'TCL', TECHNISAT: 'TechniSAT', TECNO: 'Tecno', TESLA: 'Tesla', @@ -255,6 +259,7 @@ const Vendor = Object.freeze({ const Engine = Object.freeze({ AMAYA: 'Amaya', + ARKWEB: 'ArkWeb', BLINK: 'Blink', EDGEHTML: 'EdgeHTML', FLOW: 'Flow', @@ -321,6 +326,7 @@ const OS = Object.freeze({ NETRANGE: 'NetRange', NETTV: 'NetTV', NINTENDO: 'Nintendo', + OPENHARMONY: 'OpenHarmony', OPENBSD: 'OpenBSD', OPENVMS: 'OpenVMS', OS2: 'OS/2', @@ -347,7 +353,6 @@ const OS = Object.freeze({ UBUNTU: 'Ubuntu', UNIX: 'Unix', VECTORLINUX: 'VectorLinux', - VIERA: 'Viera', WATCHOS: 'watchOS', WEBOS: 'WebOS', WINDOWS: 'Windows', diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index a50145daf..124fe9923 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta.3 +/* Extensions for UAParser.js v2.0.0-rc.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 192889056..bf12ddf64 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-beta.3 +/* Extensions for UAParser.js v2.0.0-rc.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -23,6 +23,7 @@ const CLI = 'cli'; const EMAIL = 'email'; const FETCHER = 'fetcher'; const INAPP = 'inapp'; +const MEDIAPLAYER = 'mediaplayer'; const MODULE = 'module'; ////////////////////// @@ -42,45 +43,48 @@ const CLIs = Object.freeze({ const Crawlers = Object.freeze({ browser : [ - // Amazonbot - https://developer.amazon.com/amazonbot - // Applebot - http://apple.com/go/applebot - // Bingbot - http://www.bing.com/bingbot.htm - // DuckDuckBot - http://duckduckgo.com/duckduckbot.html - // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ - // GPTBot - https://platform.openai.com/docs/gptbot - // MJ12bot - https://mj12bot.com/ - // SemrushBot - http://www.semrush.com/bot.html - [/((?:amazon|apple|bing|duckduck|facebook|gpt|mj12|semrush)bot)\/([\w\.]+)/i], - [NAME, VERSION, [TYPE, CRAWLER]], + [ + // AhrefsBot - https://ahrefs.com/robot + // Amazonbot - https://developer.amazon.com/amazonbot + // Applebot - http://apple.com/go/applebot + // Bingbot - http://www.bing.com/bingbot.htm + // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot + // DuckDuckBot - http://duckduckgo.com/duckduckbot.html + // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ + // GPTBot - https://platform.openai.com/docs/gptbot + // MJ12bot - https://mj12bot.com/ + // OpenAI Search - https://platform.openai.com/docs/bots + // SemrushBot - http://www.semrush.com/bot.html + /((?:ahrefs|amazon|apple|bing|dot|duckduck|facebook|gpt|mj12|oai-search|semrush)bot)\/([\w\.]+)/i, - // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 - [/(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i], - [NAME, VERSION, [TYPE, CRAWLER]], + // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 + /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, - // Bytespider - // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - [/((?:bytespider|(?=yahoo! )slurp))/i], - [NAME, [TYPE, CRAWLER]], + // ClaudeBot + /(claude(?:bot|-web))\/([\w\.]+)/i, - // ClaudeBot - [/(claude(?:bot|-web))\/([\w\.]+)/i], - [NAME, VERSION, [TYPE, CRAWLER]], + // Coc Coc Bot - https://help.coccoc.com/en/search-engine + /(coccocbot-(?:image|web))\/([\w\.]+)/i, + + // Googlebot - http://www.google.com/bot.html + /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i, + + // Sogou Spider + /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, + + // Yahoo! Japan - https://support.yahoo-net.jp/PccSearch/s/article/H000007955 + /(y!?j-(?:asr|br[uw]|dscv|mmp|vsidx|wsc))\/([\w\.]+)/i, + + // Yandex Bots - https://yandex.com/bots + /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i + ], - // Googlebot - http://www.google.com/bot.html - [ - /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i - ], - [NAME, VERSION, [TYPE, CRAWLER]], - - // Sogou Spider - [/(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, CRAWLER]], - // Yandex Bots - https://yandex.com/bots - [ - /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i - ], - [NAME, VERSION, [TYPE, CRAWLER]] + // Bytespider + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + [/((?:bytespider|(?=yahoo! )slurp))/i], + [NAME, [TYPE, CRAWLER]] ] }); @@ -181,29 +185,30 @@ const Emails = Object.freeze({ const Fetchers = Object.freeze({ browser : [ - // BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot - [/(bingpreview|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i], - [NAME, VERSION, [TYPE, FETCHER]], - - // Google Bots / Snapchat - [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], - [NAME, [TYPE, FETCHER]], - + [ + // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit + // ChatGPT-User - https://platform.openai.com/docs/plugins/bot + // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / Telegrambot / Twitterbot / UptimeRobot + /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot)\/([\w\.]+)/i, - // Slackbot - https://api.slack.com/robots - [/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], - [NAME, VERSION, [TYPE, FETCHER]], + // Slackbot - https://api.slack.com/robots + /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, + + // WhatsApp + /(whatsapp)\/([\w\.]+)[\/ ][ianw]/i, - // WhatsApp - [/(whatsapp)\/([\w\.]+)[\/ ][ianw]/i], - [NAME, VERSION, [TYPE, FETCHER]], + // Yahoo! Japan + /(y!?j-dlc)\/([\w\.]+)/i, - // Yandex Bots - https://yandex.com/bots - [ + // Yandex Bots - https://yandex.com/bots /(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, /(yandex(?:sitelinks|userproxy))/i ], - [NAME, VERSION, [TYPE, FETCHER]] + [NAME, VERSION, [TYPE, FETCHER]], + + // Google Bots / Snapchat + [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], + [NAME, [TYPE, FETCHER]], ] }); @@ -213,7 +218,11 @@ const Fetchers = Object.freeze({ const InApps = Object.freeze({ browser : [ - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]] + // Slack + [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]], + + // Yahoo! Japan + [/jp\.co\.yahoo\.android\.yjtop\/([\d\.]+)/i], [VERSION, 'Yahoo! Japan', [TYPE, INAPP]] ] }); @@ -226,13 +235,13 @@ const MediaPlayers = Object.freeze({ /(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia /(coremedia) v([\w\._]+)/i - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i, // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC @@ -240,90 +249,90 @@ const MediaPlayers = Object.freeze({ /(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD /(lg player|nexplayer)\s([\d\.]+)/i, /player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(nexplayer)\s([\w\.-]+)/i // Nexplayer - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(flrp)\/([\w\.-]+)/i // Flip Player - ], [[NAME, 'Flip Player'], VERSION], [ + ], [[NAME, 'Flip Player'], VERSION, [TYPE, MEDIAPLAYER]], [ /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit - ], [NAME], [ + ], [NAME, [TYPE, MEDIAPLAYER]], [ /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i // Gstreamer - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i, // Java/urllib/requests/wget/cURL /(lavf)([\d\.]+)/i // Lavf (FFMPEG) - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(htc_one_s)\/([\d\.]+)/i, // HTC One S - ], [[NAME, /_/g, ' '], VERSION], [ + ], [[NAME, /_/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i, // MPlayer SVN - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer /(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(mplayer)/i, // MPlayer (no other info) /(yourmuze)/i, // YourMuze /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime - ], [NAME], [ + ], [NAME, [TYPE, MEDIAPLAYER]], [ /(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(nokia\d+)\/([\w\.-]+)/i // Nokia - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(winamp)3 version ([\w\.-]+)/i, // Winamp /(winamp)\s([\w\.-]+)/i, /(winamp)mpeg\/([\w\.-]+)/i - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) // inlight radio - ], [NAME], [ + ], [NAME, [TYPE, MEDIAPLAYER]], [ /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i // QuickTime/RealMedia/RadioApp/RadioClientApplication/ // SoundTap/Totem/Stagefright/Streamium - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(smp)([\d\.]+)/i // SMP - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(vlc) media player - version ([\w\.]+)/i, // VLC Videolan /(vlc)\/([\w\.-]+)/i, /(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp /(foobar2000)\/([\d\.]+)/i, // Foobar2000 /(itunes)\/([\d\.]+)/i // iTunes - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(wmplayer)\/([\w\.-]+)/i, // Windows Media Player /(windows-media-player)\/([\w\.-]+)/i - ], [[NAME, /-/g, ' '], VERSION], [ + ], [[NAME, /-/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ /windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i, // Windows Media Server - ], [VERSION, [NAME, 'Windows']], [ + ], [VERSION, [NAME, 'Windows'], [TYPE, MEDIAPLAYER]], [ /(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm - ], [NAME, VERSION], [ + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(rad.io)\s([\d\.]+)/i, // Rad.io /(radio.(?:de|at|fr))\s([\d\.]+)/i - ], [[NAME, 'rad.io'], VERSION] + ], [[NAME, 'rad.io'], VERSION, [TYPE, MEDIAPLAYER]] ] }); @@ -338,7 +347,21 @@ const Modules = Object.freeze({ ] }); +////////// +// BOTS +///////// + +const Bots = Object.freeze({ + browser : [ + ...CLIs.browser, + ...Crawlers.browser, + ...Fetchers.browser, + ...Modules.browser + ] +}); + export { + Bots, CLIs, Crawlers, ExtraDevices, diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 8582fe5b4..61c63bbb6 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-beta.3 +/* Helpers for UAParser.js v2.0.0-rc.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index 95d6c0f17..886d0dc31 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-beta.3 +/* Helpers for UAParser.js v2.0.0-rc.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -11,16 +11,55 @@ /*jshint esversion: 6 */ -import { CPU, OS, Engine } from '../enums/ua-parser-enums.mjs'; +import { CPU, OS, Engine } from './enums/ua-parser-enums.mjs'; +import { UAParser } from './main/ua-parser.mjs'; +import { isFromEU } from 'detect-europe-js'; -const isAppleSilicon = (res) => res.os.is(OS.MACOS) && res.cpu.is(CPU.ARM); +const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isChromiumBased = (res) => res.engine.is(Engine.BLINK); +const isAppleSilicon = (res) => { + if (res.os.is(OS.MACOS)) { + if (res.cpu.is(CPU.ARM)) { + return true; + } + try { + const canvas = document.createElement('canvas'); + const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + const debug = webgl.getExtension('WEBGL_debug_renderer_info'); + const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL); + if (renderer.match(/apple m\d/i)) { + return true; + } + } catch { + return false; + } + } + return false; +} + +const isChromeFamily = (res) => res.engine.is(Engine.BLINK); + +const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js + / electron\//i.test(navigator?.userAgent)); // browser const isFrozenUA = (ua) => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); +const isStandalonePWA = () => window && (window.matchMedia('(display-mode: standalone)').matches || + // iOS + navigator.standalone || + // Android + document.referrer.startsWith('android-app://') || + // Windows + window.Windows || + /trident.+(msapphost|webview)\//i.test(navigator.userAgent) || + document.referrer.startsWith('app-info://platform/microsoft-store')); + export { + getDeviceVendor, isAppleSilicon, - isChromiumBased, - isFrozenUA + isChromeFamily, + isElectron, + isFromEU, + isFrozenUA, + isStandalonePWA } \ No newline at end of file diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0eb24efd1..07d59086a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta.3 +/* UAParser.js v2.0.0-rc.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-beta.3', + var LIBVERSION = '2.0.0-rc.1', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index e0e4f4ed3..e9861fc5c 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,12 +3,12 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-beta.3 +/* UAParser.js v2.0.0-rc.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. - Demo : https://faisalman.github.io/ua-parser-js + Demo : https://uaparser.dev Source : https://github.com/faisalman/ua-parser-js */ ///////////////////////////////////////////////////////////////////////////////// @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-beta.3', + var LIBVERSION = '2.0.0-rc.1', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -84,6 +84,7 @@ PREFIX_MOBILE = 'Mobile ', SUFFIX_BROWSER = ' Browser', CHROME = 'Chrome', + CHROMECAST = 'Chromecast', EDGE = 'Edge', FIREFOX = 'Firefox', OPERA = 'Opera', @@ -329,11 +330,13 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ + /quark(?:pc)?\/([-\w\.]+)/i // Quark + ], [VERSION, [NAME, 'Quark']], [ /\bddg\/([\w\.]+)/i // DuckDuckGo ], [VERSION, [NAME, 'DuckDuckGo']], [ /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser @@ -499,8 +502,8 @@ // Samsung /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [ - /\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, - /samsung[- ]([-\w]+)/i, + /\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, + /samsung[- ]((?!sm-[lr])[-\w]+)/i, /sec-(sgh\w+)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ @@ -531,7 +534,7 @@ /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models - /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi + /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i // Xiaomi Mi ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets @@ -599,7 +602,7 @@ // Amazon /(alexa)webm/i, - /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i, // Kindle Fire without Silk / Echo Show + /(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [ /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone @@ -628,6 +631,17 @@ /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ + // TCL + /tcl (xess p17aa)/i, + /droid [\w\.]+; ((?:8[14]9[16]|9(?:0(?:48|60|8[01])|1(?:3[27]|66)|2(?:6[69]|9[56])|466))[gqswx])(_\w(\w|\w\w))?(\)| bui)/i + ], [MODEL, [VENDOR, 'TCL'], [TYPE, TABLET]], [ + /droid [\w\.]+; (418(?:7d|8v)|5087z|5102l|61(?:02[dh]|25[adfh]|27[ai]|56[dh]|59k|65[ah])|a509dl|t(?:43(?:0w|1[adepqu])|50(?:6d|7[adju])|6(?:09dl|10k|12b|71[efho]|76[hjk])|7(?:66[ahju]|67[hw]|7[045][bh]|71[hk]|73o|76[ho]|79w|81[hks]?|82h|90[bhsy]|99b)|810[hs]))(_\w(\w|\w\w))?(\)| bui)/i + ], [MODEL, [VENDOR, 'TCL'], [TYPE, MOBILE]], [ + + // itel + /(itel) ((\w+))/i + ], [[VENDOR, lowerize], MODEL, [TYPE, strMapper, { 'tablet' : ['p10001l', 'w7001'], '*' : 'mobile' }]], [ + // Acer /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [ @@ -641,6 +655,10 @@ /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [ + // Nothing + /droid.+; (a(?:015|06[35]|142p?))/i + ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ + // MIXED /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron @@ -685,8 +703,14 @@ ], [[VENDOR, LG], [TYPE, SMARTTV]], [ /(apple) ?tv/i // Apple TV ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ - /crkey/i // Google Chromecast - ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey.*devicetype\/chromecast/i // Google Chromecast Third Generation + ], [[MODEL, CHROMECAST+' Third Generation'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey.*devicetype\/([^/]*)/i // Google Chromecast with specific device type + ], [[MODEL, /^/, 'Chromecast '], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /fuchsia.*crkey/i // Google Chromecast Nest Hub + ], [[MODEL, CHROMECAST+' Nest Hub'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey/i // Google Chromecast, Linux-based or unknown + ], [[MODEL, CHROMECAST], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ /\(dtv[\);].+(aquos)/i, @@ -722,6 +746,8 @@ // WEARABLES /////////////////// + /\b(sm-[lr]\d\d[05][fnuw]?s?)\b/i // Samsung Galaxy Watch + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [ /((pebble))app/i // Pebble ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch @@ -735,7 +761,9 @@ /droid.+; (glass) \d/i // Google Glass ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ - /(quest( \d| pro)?)/i // Oculus Quest + /(pico) (4|neo3(?: link|pro)?)/i // Pico + ], [VENDOR, MODEL, [TYPE, XR]], [ + /; (quest( \d| pro)?)/i // Oculus Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ /////////////////// @@ -768,6 +796,9 @@ /windows.+ edge\/([\w\.]+)/i // EdgeHTML ], [VERSION, [NAME, EDGE+'HTML']], [ + /(arkweb)\/([\w\.]+)/i // ArkWeb + ], [NAME, VERSION], [ + /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink ], [VERSION, [NAME, 'Blink']], [ @@ -804,10 +835,22 @@ /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ + // Google Chromecast + /android ([\d\.]+).*crkey/i // Google Chromecast, Android-based + ], [VERSION, [NAME, CHROMECAST + ' Android']], [ + /fuchsia.*crkey\/([\d\.]+)/i // Google Chromecast, Fuchsia-based + ], [VERSION, [NAME, CHROMECAST + ' Fuchsia']], [ + /crkey\/([\d\.]+).*devicetype\/smartspeaker/i // Google Chromecast, Linux-based Smart Speaker + ], [VERSION, [NAME, CHROMECAST + ' SmartSpeaker']], [ + /linux.*crkey\/([\d\.]+)/i // Google Chromecast, Legacy Linux-based + ], [VERSION, [NAME, CHROMECAST + ' Linux']], [ + /crkey\/([\d\.]+)/i // Google Chromecast, unknown + ], [VERSION, [NAME, CHROMECAST]], [ + // Mobile OSes /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS - ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS - /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i, + ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS/OpenHarmony + /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish|openharmony)[-\/ ]?([\w\.]*)/i, /(blackberry)\w*\/([\w\.]*)/i, // Blackberry /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS /\((series40);/i // Series 40 @@ -824,9 +867,7 @@ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS ], [VERSION, [NAME, 'watchOS']], [ - // Google Chromecast - /crkey\/([\d\.]+)/i // Google Chromecast - ], [VERSION, [NAME, CHROME+'cast']], [ + // Google ChromeOS /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS ], [[NAME, "Chrome OS"], VERSION],[ @@ -838,6 +879,7 @@ // Console /(nintendo|playstation) (\w+)/i, // Nintendo/Playstation /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) + /(pico) .+os([\w\.]+)/i, // Pico // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm From 7a754ef227cbee03d1da1fd1e518a10978cbc27a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 2 Nov 2024 12:27:42 +0700 Subject: [PATCH 242/388] Update browser.type="inapp" for InApp browsers --- src/main/ua-parser.js | 19 ++++++---- test/mocha-test.js | 2 +- test/specs/browser-all.json | 69 ++++++++++++++++++++++++------------- 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 07d59086a..8290e5cdf 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -40,6 +40,7 @@ WEARABLE = 'wearable', XR = 'xr', EMBEDDED = 'embedded', + INAPP = 'inapp', USER_AGENT = 'user-agent', UA_MAX_LENGTH = 500, BRANDS = 'brands', @@ -385,13 +386,12 @@ /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser ], [NAME, VERSION], [ - /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq - /\[(linkedin)app\]/i // LinkedIn App for iOS & Android + /(lbbrowser|rekonq)/i // LieBao Browser/Rekonq ], [NAME], [ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android - ], [[NAME, FACEBOOK], VERSION], [ + ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [ /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp @@ -399,12 +399,17 @@ /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter - /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat - ], [NAME, VERSION], [ + /(instagram|snapchat)[\/ ]([-\w\.]+)/i // Instagram/Snapchat + ], [NAME, VERSION, [TYPE, INAPP]], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS - ], [VERSION, [NAME, 'GSA']], [ + ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [ /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok - ], [VERSION, [NAME, 'TikTok']], [ + ], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [ + /\[(linkedin)app\]/i // LinkedIn App for iOS & Android + ], [NAME, [TYPE, INAPP]], [ + + /(chromium)[\/ ]([-\w\.]+)/i // Chromium + ], [NAME, VERSION], [ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ diff --git a/test/mocha-test.js b/test/mocha-test.js index 7b5e71173..68213ac9b 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -17,7 +17,7 @@ var methods = [ title : 'getBrowser', label : 'browser', list : browsers, - properties : ['name', 'major', 'version'] + properties : ['name', 'major', 'version', 'type'] }, { title : 'getCPU', diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index d8638b06f..a69e84892 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -16,7 +16,8 @@ { "name" : "Alipay", "version" : "10.2.51.7100", - "major" : "10" + "major" : "10", + "type" : "inapp" } }, { @@ -26,7 +27,8 @@ { "name" : "Alipay", "version" : "10.3.50.9999", - "major" : "10" + "major" : "10", + "type" : "inapp" } }, { @@ -536,7 +538,8 @@ { "name" : "Facebook", "version" : "35.0.0.48.273", - "major" : "35" + "major" : "35", + "type" : "inapp" } }, { @@ -546,7 +549,8 @@ { "name" : "Facebook", "version" : "91.0.0.41.73", - "major" : "91" + "major" : "91", + "type" : "inapp" } }, { @@ -556,7 +560,8 @@ { "name" : "Facebook", "version" : "undefined", - "major" : "undefined" + "major" : "undefined", + "type" : "inapp" } }, { @@ -566,7 +571,8 @@ { "name" : "Klarna", "version" : "23.36.223", - "major" : "23" + "major" : "23", + "type" : "inapp" } }, { @@ -576,7 +582,8 @@ { "name" : "Klarna", "version" : "23.36.215", - "major" : "23" + "major" : "23", + "type" : "inapp" } }, { @@ -586,7 +593,8 @@ { "name" : "Instagram", "version" : "142.0.0.22.109", - "major" : "142" + "major" : "142", + "type" : "inapp" } }, { @@ -975,7 +983,8 @@ { "name" : "Line", "version" : "6.5.1", - "major" : "6" + "major" : "6", + "type" : "inapp" } }, { @@ -985,7 +994,8 @@ { "name" : "Line", "version" : "8.4.1", - "major" : "8" + "major" : "8", + "type" : "inapp" } }, { @@ -2056,7 +2066,8 @@ { "name" : "GSA", "version" : "30.1.161623614", - "major" : "30" + "major" : "30", + "type" : "inapp" } }, { @@ -2152,7 +2163,8 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [LinkedInApp]", "expect" : { - "name" : "LinkedIn" + "name" : "LinkedIn", + "type" : "inapp" } }, { @@ -2257,7 +2269,8 @@ "expect" : { "name" : "KAKAOTALK", "version": "2409760", - "major" : "2409760" + "major" : "2409760", + "type" : "inapp" } }, { @@ -2266,7 +2279,8 @@ "expect" : { "name" : "KAKAOSTORY", "version": "6.8.3_21046", - "major" : "6" + "major" : "6", + "type" : "inapp" } }, { @@ -2275,7 +2289,8 @@ "expect" : { "name" : "KAKAOTALK", "version": "9.7.6", - "major" : "9" + "major" : "9", + "type" : "inapp" } }, { @@ -2284,7 +2299,8 @@ "expect" : { "name" : "NAVER", "version": "11.11.2", - "major" : "11" + "major" : "11", + "type" : "inapp" } }, { @@ -2293,7 +2309,8 @@ "expect" : { "name" : "NAVER", "version": "10.25.0", - "major" : "10" + "major" : "10", + "type" : "inapp" } }, { @@ -2302,7 +2319,8 @@ "expect" : { "name" : "TikTok", "version": "21.9.4", - "major" : "21" + "major" : "21", + "type" : "inapp" } }, { @@ -2311,7 +2329,8 @@ "expect" : { "name" : "TikTok", "version": "21.1.0", - "major" : "21" + "major" : "21", + "type" : "inapp" } }, { @@ -2320,7 +2339,8 @@ "expect" : { "name" : "TikTok", "version": "28.3.4", - "major" : "28" + "major" : "28", + "type" : "inapp" } }, { @@ -2360,7 +2380,8 @@ { "name" : "Snapchat", "version" : "12.33.0.36", - "major" : "12" + "major" : "12", + "type" : "inapp" } }, { @@ -2370,7 +2391,8 @@ { "name" : "Twitter", "version" : "undefined", - "major" : "undefined" + "major" : "undefined", + "type" : "inapp" } }, { @@ -2380,7 +2402,8 @@ { "name" : "Twitter", "version" : "10.34", - "major" : "10" + "major" : "10", + "type" : "inapp" } } ] \ No newline at end of file From 5a1d0319f811e71c09b41e99d4f37d37f59fca48 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 2 Nov 2024 13:15:33 +0700 Subject: [PATCH 243/388] Add new helper method: `isBot()` to detect whether current browser is a bot --- src/helpers/ua-parser-helpers.d.ts | 2 ++ src/helpers/ua-parser-helpers.js | 3 +++ test/mocha-test-helpers.js | 20 +++++++++++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index e8dd9d358..7074c229d 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -6,6 +6,7 @@ import { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; declare function isAppleSilicon(res: IResult): boolean; +declare function isBot(res: IResult): boolean; declare function isChromeFamily(res: IResult): boolean; declare function isElectron(): boolean; declare function isFromEU(): boolean; @@ -15,6 +16,7 @@ declare function isStandalonePWA(): boolean; export { getDeviceVendor, isAppleSilicon, + isBot, isChromeFamily, isElectron, isFromEU, diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 61c63bbb6..d5e35a00c 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -33,6 +33,8 @@ const isAppleSilicon = (res) => { return false; } +const isBot = (res) => ['cli', 'crawler', 'fetcher', 'module'].includes(res.browser.type); + const isChromeFamily = (res) => res.engine.is(Engine.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js @@ -53,6 +55,7 @@ const isStandalonePWA = () => window && (window.matchMedia('(display-mode: stand module.exports = { getDeviceVendor, isAppleSilicon, + isBot, isChromeFamily, isElectron, isFromEU, diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index da9ae2c85..ab61cd939 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,6 +1,7 @@ const assert = require('assert'); const { UAParser } = require('../src/main/ua-parser'); -const { getDeviceVendor, isAppleSilicon, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); +const { getDeviceVendor, isAppleSilicon, isBot, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); +const { Bots, Emails } = require('../src/extensions/ua-parser-extensions'); describe('getDeviceVendor', () => { it('Can guess the device vendor from a model name', () => { @@ -31,6 +32,23 @@ describe('isAppleSilicon', () => { }); }); +describe('isBot', () => { + it('Can detect Bots', () => { + + // non-real ua + const ahrefsBot = 'Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)'; + const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; + const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; + const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; + + const botParser = new UAParser(firefox, { Bots, Emails }); + assert.equal(isBot(botParser.getResult()), false); + assert.equal(isBot(botParser.setUA(ahrefsBot).getResult()), true); + assert.equal(isBot(botParser.setUA(scrapy).getResult()), true); + assert.equal(isBot(botParser.setUA(thunderbird).getResult()), false); + }); +}); + describe('isChromeFamily', () => { it('Can detect Chromium-based browser', () => { From 10b81836f106a8c0c245bfba464e5bebbe4f5878 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 2 Nov 2024 18:26:35 +0700 Subject: [PATCH 244/388] Improve browser detection: SRWare Iron --- src/main/ua-parser.js | 4 +++- test/specs/browser-all.json | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 8290e5cdf..95914f135 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -328,7 +328,7 @@ /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer - // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon + // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio)\/([-\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi @@ -388,6 +388,8 @@ ], [NAME, VERSION], [ /(lbbrowser|rekonq)/i // LieBao Browser/Rekonq ], [NAME], [ + /ome\/([\w\.]+) \w* ?(iron) saf/i // Iron + ], [VERSION, NAME], [ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index a69e84892..edf0d8b0e 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -856,6 +856,26 @@ "major" : "22" } }, + { + "desc" : "Iron", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Iron Safari/537.36", + "expect" : + { + "name" : "Iron", + "version" : "129.0.0.0", + "major" : "129" + } + }, + { + "desc" : "Iron", + "ua" : "Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Iron Safari/537.36", + "expect" : + { + "name" : "Iron", + "version" : "113.0.0.0", + "major" : "113" + } + }, { "desc" : "Jasmine", "ua" : "SAMSUNG-S8000/S8000XXIF3 SHP/VPP/R5 Jasmine/1.0 Nextreaming SMM-MMS/1.2.0 profile/MIDP-2.1 configuration/CLDC-1.1", From b7c2f541d9ce000f031e04efd150df96d625a1eb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 2 Nov 2024 19:53:09 +0700 Subject: [PATCH 245/388] Add new browsers: Slimjet & SlimBoat https://www.slimjet.com/ --- src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 2 +- test/specs/browser-all.json | 22 +++++++++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 4fc06b4d0..be4b2afb6 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -122,7 +122,9 @@ const Browser = Object.freeze({ SILK: 'Silk', SKYFIRE: 'Skyfire', SLEIPNIR: 'Sleipnir', + SLIMBOAT: 'SlimBoat', SLIMBROWSER: 'SlimBrowser', + SLIMJET: 'Slimjet', SNAPCHAT: 'Snapchat', SOGOU_EXPLORER: 'Sogou Explorer', SOGOU_MOBILE: 'Sogou Mobile', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 95914f135..937d197d3 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -325,7 +325,7 @@ /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir // Trident based - /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser + /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index edf0d8b0e..b8fd74be5 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1629,16 +1629,36 @@ "major" : "2" } }, + { + "desc" : "SlimBoat", + "ua" : "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/534.34 (KHTML, like Gecko) SlimBoat/1.1.23 Chrome/11.0.696.7 Version/5.1 Safari/534.34", + "expect" : + { + "name" : "SlimBoat", + "version" : "1.1.23", + "major" : "1" + } + }, { "desc" : "SlimBrowser", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SlimBrowser)", "expect" : { - "name" : "Slim", + "name" : "SlimBrowser", "version" : "undefined", "major" : "undefined" } }, + { + "desc" : "Slimjet", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36 Slimjet/20.0.2.0", + "expect" : + { + "name" : "Slimjet", + "version" : "20.0.2.0", + "major" : "20" + } + }, { "desc" : "Swiftfox", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0 (Swiftfox)", From d571859b2e1f0d323a584eefdd637e4af1696582 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 2 Nov 2024 20:08:10 +0700 Subject: [PATCH 246/388] Improve browser detection: `Comodo Dragon`, or simply `Dragon` --- src/enums/ua-parser-enums.js | 2 +- src/main/ua-parser.js | 6 ++---- test/specs/browser-all.json | 12 +++++++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index be4b2afb6..4ba852563 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -32,11 +32,11 @@ const Browser = Object.freeze({ CHROMIUM: 'Chromium', COBALT: 'Cobalt', COC_COC: 'Coc Coc', - COMODO_DRAGON: 'Comodo Dragon', CONKEROR: 'Conkeror', DILLO: 'Dillo', DOLPHIN: 'Dolphin', DORIS: 'Doris', + DRAGON: 'Dragon', DUCKDUCKGO: 'DuckDuckGo', EDGE: 'Edge', EPIPHANY: 'Epiphany', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 937d197d3..7120be2f4 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -329,8 +329,8 @@ /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ @@ -376,8 +376,6 @@ ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser /samsungbrowser\/([\w\.]+)/i // Samsung Internet ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ - /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon - ], [[NAME, /_/g, ' '], VERSION], [ /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer ], [VERSION, [NAME, SOGOU + ' Explorer']], [ /(sogou)mo\w+\/([\d\.]+)/i // Sogou Mobile diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index b8fd74be5..abafca798 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -406,11 +406,21 @@ "ua" : "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/535.7 (KHTML, like Gecko) Comodo_Dragon/16.1.1.0 Chrome/16.0.912.63 Safari/535.7", "expect" : { - "name" : "Comodo Dragon", + "name" : "Dragon", "version" : "16.1.1.0", "major" : "16" } }, + { + "desc" : "Comodo Dragon", + "ua" : "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/98.0.4758.102 Chrome/98.0.4758.102 Safari/537.36", + "expect" : + { + "name" : "Dragon", + "version" : "98.0.4758.102", + "major" : "98" + } + }, { "desc" : "Conkeror", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:6.0.1) Gecko/20110831 conkeror/0.9.3", From 2624a92b39cc89cbae7cf3deb35cab9b806d4c94 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 3 Nov 2024 01:15:06 +0700 Subject: [PATCH 247/388] Improve browser detection: `2345` & `360` --- src/enums/ua-parser-enums.js | 4 +-- src/main/ua-parser.js | 9 ++--- test/specs/browser-all.json | 66 +++++++++++++++++++++++++++++------- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 4ba852563..a81f623c5 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -8,8 +8,8 @@ /*jshint esversion: 6 */ const Browser = Object.freeze({ - '2345_EXPLORER': '2345Explorer', - '360': '360 Browser', + '2345': '2345', + '360': '360', ALIPAY: 'Alipay', AMAYA: 'Amaya', ANDROID: 'Android Browser', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7120be2f4..babc7e5cd 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -368,8 +368,8 @@ ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [ /fxios\/([\w\.-]+)/i // Firefox for iOS ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ - /\bqihu|(qi?ho?o?|360)browser/i // 360 - ], [[NAME, '360' + SUFFIX_BROWSER]], [ + /\bqihoobrowser\/?([\w\.]*)/i // 360 + ], [VERSION, [NAME, '360']], [ /\b(qq)\/([\w\.]+)/i // QQ ], [[NAME, /(.+)/, '$1Browser'], VERSION], [ /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i @@ -382,11 +382,12 @@ ], [[NAME, SOGOU + ' Mobile'], VERSION], [ /(electron)\/([\w\.]+) safari/i, // Electron-based App /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla - /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser + /m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i // QQ/2345 ], [NAME, VERSION], [ /(lbbrowser|rekonq)/i // LieBao Browser/Rekonq ], [NAME], [ - /ome\/([\w\.]+) \w* ?(iron) saf/i // Iron + /ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron + /ome\/([\w\.]+).+qihu (360)[es]e/i // 360 ], [VERSION, NAME], [ // WebView diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index abafca798..db978de7c 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1,12 +1,62 @@ [ + { + "desc" : "2345 Browser", + "ua" : "Mozilla/5.0 (Linux; Android 7.0; MI NOTE Pro Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36 Mb2345Browser/15.6.2", + "expect" : + { + "name" : "2345", + "version" : "15.6.2", + "major" : "15" + } + }, + { + "desc" : "2345 Chrome", + "ua" : "Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 (KHTML like Gecko) Chrome/39.0.2171.99 Safari/537.36 2345chrome v3.0.0.9739", + "expect" : + { + "name" : "2345", + "version" : "3.0.0.9739", + "major" : "3" + } + }, + { + "desc" : "2345 Explorer", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.90 Safari/537.36 2345Explorer/9.2.1.17116", + "expect" : + { + "name" : "2345", + "version" : "9.2.1.17116", + "major" : "9" + } + }, { "desc" : "360 Browser on iOS", "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_1 like Mac OS X) AppleWebKit/607.3.9 (KHTML, like Gecko) Mobile/16G102 QHBrowser/317 QihooBrowser/4.0.10", "expect" : { - "name" : "360 Browser", - "version" : "undefined", - "major" : "undefined" + "name" : "360", + "version" : "4.0.10", + "major" : "4" + } + }, + { + "desc" : "360 Secure Browser on Windows 10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 QIHU 360SE", + "expect" : + { + "name" : "360", + "version" : "86.0.4240.198", + "major" : "86" + } + }, + { + "desc" : "360 Speed Browser on Windows 10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 QIHU 360EE", + "expect" : + { + "name" : "360", + "version" : "86.0.4240.198", + "major" : "86" } }, { @@ -2148,16 +2198,6 @@ "name" : "LBBROWSER" } }, - { - "desc" : "2345 Browser", - "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.90 Safari/537.36 2345Explorer/9.2.1.17116", - "expect" : - { - "name" : "2345Explorer", - "version" : "9.2.1.17116", - "major" : "9" - } - }, { "desc" : "QQBrowserLite", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14 QQBrowserLite/1.1.0", From 2dc1be1a50c354d1723cf21bc21ad5b3d88969e9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 3 Nov 2024 01:34:55 +0700 Subject: [PATCH 248/388] Add new browser: `115 Browser` http://pc.115.com/ --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 2 +- test/specs/browser-all.json | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index a81f623c5..5cbf3ac17 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -8,6 +8,7 @@ /*jshint esversion: 6 */ const Browser = Object.freeze({ + '115': '115', '2345': '2345', '360': '360', ALIPAY: 'Alipay', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index babc7e5cd..c0d7907d0 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -331,7 +331,7 @@ // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon)\/([-\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon - /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi + /(heytap|ovi|115)browser\/([\d\.]+)/i, // HeyTap/Ovi/115 /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ /quark(?:pc)?\/([-\w\.]+)/i // Quark diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index db978de7c..ebf10d9cd 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1,4 +1,14 @@ [ + { + "desc" : "115 Browser", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/24.3.0.3", + "expect" : + { + "name" : "115", + "version" : "24.3.0.3", + "major" : "24" + } + }, { "desc" : "2345 Browser", "ua" : "Mozilla/5.0 (Linux; Android 7.0; MI NOTE Pro Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36 Mb2345Browser/15.6.2", From 246c0388950ce2ff789294953ec955301e4ef661 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 3 Nov 2024 11:17:30 +0700 Subject: [PATCH 249/388] Improve browser detection: `Maxthon` --- src/main/ua-parser.js | 2 ++ test/specs/browser-all.json | 62 ++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index c0d7907d0..b2beb6828 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -321,6 +321,8 @@ // Mixed /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu ], [VERSION, [NAME, 'Baidu']], [ + /\b(?:mxbrowser|mxios|myie2)\/?([-\w\.]*)\b/i // Maxthon + ], [VERSION, [NAME, 'Maxthon']], [ /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index ebf10d9cd..25bb48fd4 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1119,7 +1119,57 @@ } }, { - "desc" : "Maxthon", + "desc" : "Maxthon on Android", + "ua" : "Mozilla/5.0 (Linux; Android 5.1.1; KFAUWI Build/LVY48F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.127 Safari/537.36 MxBrowser/4.3.5.2000", + "expect" : + { + "name" : "Maxthon", + "version" : "4.3.5.2000", + "major" : "4" + } + }, + { + "desc" : "Maxthon on iOS", + "ua" : "Mozilla/5.0 (iPad; CPU OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/10.0 Mobile/15E148 Safari/602.1 MXiOS/5.4.5.2", + "expect" : + { + "name" : "Maxthon", + "version" : "5.4.5.2", + "major" : "5" + } + }, + { + "desc" : "Maxthon on Linux", + "ua" : "Mozilla/5.0 (X11; Linux i686; Ubuntu 14.04.3 LTS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36", + "expect" : + { + "name" : "Maxthon", + "version" : "1.0.5.3", + "major" : "1" + } + }, + { + "desc" : "Maxthon on macOS", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6 Maxthon/5.1.102", + "expect" : + { + "name" : "Maxthon", + "version" : "5.1.102", + "major" : "5" + } + }, + { + "desc" : "Maxthon on Windows Server 2003", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; MyIE2; .NET CLR 1.1.4322)", + "expect" : + { + "name" : "Maxthon", + "version" : "undefined", + "major" : "undefined" + } + }, + { + "desc" : "Maxthon on Windows XP", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 1.1.4322)", "expect" : { @@ -1128,6 +1178,16 @@ "major" : "undefined" } }, + { + "desc" : "Maxthon on Windows 10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36 Maxthon/5.2.7.2000", + "expect" : + { + "name" : "Maxthon", + "version" : "5.2.7.2000", + "major" : "5" + } + }, { "desc" : "Midori", "ua" : "Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+", From 2223a2b2d4e48e4d209ab508ede2f8b7dd7c6381 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 3 Nov 2024 11:31:22 +0700 Subject: [PATCH 250/388] Add new browser: `LibreWolf` https://librewolf.net/ --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 2 +- test/specs/browser-all.json | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 5cbf3ac17..048110746 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -75,6 +75,7 @@ const Browser = Object.freeze({ KLARNA: 'Klarna', KINDLE: 'Kindle', LENOVO: 'Smart Lenovo Browser', + LIBREWOLF: 'LibreWolf', LIEBAO: 'LBBROWSER', LINE: 'Line', LINKEDIN: 'LinkedIn', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b2beb6828..242f42a5c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -446,7 +446,7 @@ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ - /(wolvic)\/([\w\.]+)/i // Wolvic + /(wolvic|librewolf)\/([\w\.]+)/i // Wolvic/LibreWolf ], [NAME, VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality ], [VERSION, [NAME, FIREFOX+' Reality']], [ diff --git a/test/specs/browser-all.json b/test/specs/browser-all.json index 25bb48fd4..869859786 100644 --- a/test/specs/browser-all.json +++ b/test/specs/browser-all.json @@ -1066,6 +1066,26 @@ "major" : "9" } }, + { + "desc" : "LibreWolf", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:91.0) Gecko/20100101 LibreWolf/91.0", + "expect" : + { + "name" : "LibreWolf", + "version" : "91.0", + "major" : "91" + } + }, + { + "desc" : "LibreWolf", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0 LibreWolf/97.0.1", + "expect" : + { + "name" : "LibreWolf", + "version" : "97.0.1", + "major" : "97" + } + }, { "desc" : "LINE on Android", "ua" : "Mozilla/5.0 (Linux; Android 5.0; ASUS_Z00AD Build/LRX21V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/51.0.2704.81 Mobile Safari/537.36 Line/6.5.1/IAB", From 5097b8093e98f5fdaabc16fe4c8682a69771d6eb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 4 Nov 2024 11:10:44 +0700 Subject: [PATCH 251/388] [submodule:enums] Add some of Chromecast OS variants --- src/enums/ua-parser-enums.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 048110746..ea4d112b4 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -291,6 +291,10 @@ const OS = Object.freeze({ CENTOS: 'CentOS', CHROME_OS: 'Chrome OS', CHROMECAST: 'Chromecast', + CHROMECAST_ANDROID: 'Chromecast Android', + CHROMECAST_FUCHSIA: 'Chromecast Fuchsia', + CHROMECAST_LINUX: 'Chromecast Linux', + CHROMECAST_SMARTSPEAKER: 'Chromecast SmartSpeaker', CONTIKI: 'Contiki', DEBIAN: 'Debian', DEEPIN: 'Deepin', From e954f19b5c9627a6f91f85eb1ac7d7ea48b21c5b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 5 Nov 2024 10:46:48 +0700 Subject: [PATCH 252/388] Move `isFrozenUA()` & `isStandalonePWA()` to its own npm module --- package.json | 4 +++- src/helpers/ua-parser-helpers.js | 16 +++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 7674e9273..c0f93cd79 100755 --- a/package.json +++ b/package.json @@ -211,7 +211,9 @@ "test:playwright": "playwright test" }, "dependencies": { - "detect-europe-js": "^0.1.1" + "detect-europe-js": "^0.1.1", + "is-standalone-pwa": "^0.1.0", + "ua-is-frozen": "^0.1.1" }, "devDependencies": { "@babel/parser": "7.15.8", diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index d5e35a00c..c8f6925b7 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -7,9 +7,11 @@ /*jshint esversion: 6 */ -const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); const { UAParser } = require('../main/ua-parser'); +const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); const { isFromEU } = require('detect-europe-js'); +const { isFrozenUA } = require('ua-is-frozen'); +const { isStandalonePWA } = require('is-standalone-pwa'); const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; @@ -40,18 +42,6 @@ const isChromeFamily = (res) => res.engine.is(Engine.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js / electron\//i.test(navigator?.userAgent)); // browser -const isFrozenUA = (ua) => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); - -const isStandalonePWA = () => window && (window.matchMedia('(display-mode: standalone)').matches || - // iOS - navigator.standalone || - // Android - document.referrer.startsWith('android-app://') || - // Windows - window.Windows || - /trident.+(msapphost|webview)\//i.test(navigator.userAgent) || - document.referrer.startsWith('app-info://platform/microsoft-store')); - module.exports = { getDeviceVendor, isAppleSilicon, From 0808d1d940379a83529325acd7cbed4ce48a8074 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 7 Nov 2024 18:34:07 +0700 Subject: [PATCH 253/388] Fix #759 - Incorrect import path for ESM files in build script --- script/build-esm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build-esm.js b/script/build-esm.js index 68b8258a0..98f7ffb8b 100755 --- a/script/build-esm.js +++ b/script/build-esm.js @@ -7,7 +7,7 @@ const generateMJS = (module) => { let text = fs.readFileSync(src, 'utf-8'); replacements.push( - [/const (.+?)\s*=\s*require\(\'\.(.+)\'\)/ig, 'import $1 from \'$2.mjs\''], + [/const (.+?)\s*=\s*require\(\'\.(.+)\'\)/ig, 'import $1 from \'\.$2.mjs\''], [/const (.+?)\s*=\s*require\(\'(.+)\'\)/ig, 'import $1 from \'$2\''], [/module\.exports =/ig, 'export'] ); From a1644b67754d71f152bc5382c35c24b1bef8c338 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 7 Nov 2024 20:58:26 +0700 Subject: [PATCH 254/388] Update version to `2.0.0-rc.2` --- .github/ISSUE_TEMPLATE/bug_report.md | 8 +-- CHANGELOG.md | 62 ++++++++++++++++-------- dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- package-lock.json | 46 ++++++++++++++++-- package.json | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 16 ++++-- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 2 +- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 23 +++------ src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 48 ++++++++++-------- 17 files changed, 150 insertions(+), 81 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8fadfd144..1705fdbe7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,8 +10,6 @@ assignees: '' **Library version** Which version of the library that you use, eg: v0.7.35 or v2.0.0-alpha.3 -For the issue related with detection result, you can use the demo section in https://uaparser.dev to confirm - **Describe the bug** A clear and concise description of what the bug is. @@ -23,11 +21,13 @@ Steps to reproduce the behavior: 4. See error **Expected behavior** -A clear and concise description of what you expected to happen. +A clear and concise description of what you expected to happen, or what's referred in the docs https://docs.uaparser.dev/ **Screenshots** If applicable, add screenshots to help explain your problem. +For issues related to detection results, you can send the screenshots of the demo section at https://uaparser.dev/#demo to confirm. + **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] @@ -40,4 +40,4 @@ If applicable, add screenshots to help explain your problem. - Version [e.g. 22] **Additional context** -Add any other context about the problem here. +Add any other context about the problem here. \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 63d6bf0f7..52575f009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ # UAParser.js Changelog +## Migrating from v1 to v2 + +- What's breaking: + - Licensed under AGPLv3 (open-source) or PRO License (commercial) + - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` + - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` +- What's new: + - New device type: `xr`, to identify AR/VR devices + - New browser property: `browser.type`, to identify the type of the browser: `crawler`, `cli`, `email`, `fetcher`, `inapp`, `mediaplayer`, `module` + - New methods in result object (all of `get*()` return value): + - Support for client hints: `withClientHints()` + - Support for feature detection: `withFeatureCheck()` + - Utility for easy comparison: `is()` + - Utility to print full-name: `toString()` + - Parse directly from command line using `npx ua-parser-js` + - Extensions can be passed as a list to `UAParser()` + - Support for ES module & TypeScript `import { UAParser } from 'ua-parser-js'` + - Provided Enums submodule `'ua-parser-js/enums'` + - Provided Extensions submodule `'ua-parser-js/extensions'` + - Provided Helpers submodule `'ua-parser-js/helpers'`: + - `getDeviceVendor()` to guess for a device vendor based on its model name + - `isAppleSilicon()` to check if the device has Apple Silicon Mac device properties + - `isBot()` to check if the browser is identified as a bot + - `isChromeFamily()` to check if the browser is Chrome-based / has Blink engine (i.e: New Opera, New Edge, Vivaldi, Brave, Arc, etc.) + - `isElectron()` to check if current window is running inside Electron + - `isFromEU()` to check if current window is from an EU (European Union) country + - `isFrozenUA()` to check if a user-agent string match with the reduced/frozen user-agent pattern + - `isStandalonePWA()` to check if current window is a standalone PWA + +-- + +## Version 2.0.0-rc.2 + +- Fix incorrect import path in ESM files +- Add new browser: 115, SlimBoat, Slimjet, LibreWolf +- Improve browser detection: 2345, 360, Dragon, Iron, Maxthon +- `ua-parser-js/enums` submodule: + - Add Chromecast OS variants: Android/Fuchsia/Linux/SmartSpeaker +- `ua-parser-js/helpers` submodule: + - Add new method: `isBot()` to check if the browser is identified as a bot + ## Version 2.0.0-rc.1 - Fix Python Request mistakenly identified as Meta Quest @@ -68,27 +109,6 @@ - Initial work on new major version -## Version 2.0 - -- What's breaking: - - Dual-licensed under AGPLv3 or PRO License - - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` - - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` - - AR/VR devices moved to new device type: `xr` - - New property in `browser`: `type` -- What's new: - - Some new methods in result object: - - Support for client hints: `withClientHints()` - - Support for feature detection: `withFeatureCheck()` - - Utility for easy comparison: `is()` - - Utility to print full-name: `toString()` - - Parse directly from command line using `npx ua-parser-js` - - Extensions can be passed as a list to `UAParser()` - - Support for ES module `import { UAParser } from 'ua-parser-js'` - - Provided Enums submodule `'ua-parser-js/enums'` - - Provided Extensions submodule `'ua-parser-js/extensions'` - - Provided Helpers submodule `'ua-parser-js/helpers'` - --- ## Version 0.7.38 / 1.0.38 diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 4187195e4..18512030c 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-rc.1 +/* UAParser.js v2.0.0-rc.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-rc.1",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-rc.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index e22b892ce..0f145e7e3 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-rc.1 +/* UAParser.js v2.0.0-rc.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -!function(i,c){"use strict";function e(i){for(var e={},t=0;tS?Ti(i,S):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-rc.1",Ui.BROWSER=e([m,v,h,f]),Ui.CPU=e([k]),Ui.DEVICE=e([p,g,f,x,y,t,r,o,d]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===l&&define.amd?define(function(){return Ui}):ui&&(i.UAParser=Ui);var ji,Ei=ui&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(ji=new Ui,Ei.ua=ji.getResult(),Ei.ua.get=function(){return ji.getUA()},Ei.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,c){"use strict";function e(i){for(var e={},t=0;tT?Ti(i,T):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-rc.2",Ui.BROWSER=e([m,v,h,f]),Ui.CPU=e([k]),Ui.DEVICE=e([p,g,f,x,y,t,r,o,d]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===l&&define.amd?define(function(){return Ui}):ui&&(i.UAParser=Ui);var ji,Mi=ui&&(i.jQuery||i.Zepto);Mi&&!Mi.ua&&(ji=new Ui,Mi.ua=ji.getResult(),Mi.ua.get=function(){return ji.getUA()},Mi.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Mi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1c4d590fc..8d8b2bc24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "funding": [ { "type": "opencollective", @@ -23,7 +23,9 @@ ], "license": "AGPL-3.0-or-later", "dependencies": { - "detect-europe-js": "^0.1.1" + "detect-europe-js": "^0.1.1", + "is-standalone-pwa": "^0.1.0", + "ua-is-frozen": "^0.1.1" }, "bin": { "ua-parser-js": "script/cli.js" @@ -2609,6 +2611,25 @@ "node": ">=8" } }, + "node_modules/is-standalone-pwa": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-standalone-pwa/-/is-standalone-pwa-0.1.0.tgz", + "integrity": "sha512-n5SQqXd0/JEkrKYEB7ZUndwuS7NKskZvk6rZZt6kTE1jiPxtPfPvVhXkfteIKpUfcEP07qsja/Wjz9NDjiZ5gg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ] + }, "node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -4597,6 +4618,25 @@ "node": "*" } }, + "node_modules/ua-is-frozen": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ua-is-frozen/-/ua-is-frozen-0.1.1.tgz", + "integrity": "sha512-TxhyfblPzcDJXRXu/j+73OI6s1jG6PUZBF/8hjTHoAsjZYKl9IhZzLQlnZHFLe5U2mvL1lMOOmcy647KpUq25A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ] + }, "node_modules/uglify-js": { "version": "3.12.8", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", diff --git a/package.json b/package.json index c0f93cd79..1beaf501a 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ea4d112b4..63a391772 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-rc.1 +/* Enums for UAParser.js v2.0.0-rc.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 52df47f6e..6af7ef755 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-rc.1 +/* Enums for UAParser.js v2.0.0-rc.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -12,8 +12,9 @@ /*jshint esversion: 6 */ const Browser = Object.freeze({ - '2345_EXPLORER': '2345Explorer', - '360': '360 Browser', + '115': '115', + '2345': '2345', + '360': '360', ALIPAY: 'Alipay', AMAYA: 'Amaya', ANDROID: 'Android Browser', @@ -36,11 +37,11 @@ const Browser = Object.freeze({ CHROMIUM: 'Chromium', COBALT: 'Cobalt', COC_COC: 'Coc Coc', - COMODO_DRAGON: 'Comodo Dragon', CONKEROR: 'Conkeror', DILLO: 'Dillo', DOLPHIN: 'Dolphin', DORIS: 'Doris', + DRAGON: 'Dragon', DUCKDUCKGO: 'DuckDuckGo', EDGE: 'Edge', EPIPHANY: 'Epiphany', @@ -78,6 +79,7 @@ const Browser = Object.freeze({ KLARNA: 'Klarna', KINDLE: 'Kindle', LENOVO: 'Smart Lenovo Browser', + LIBREWOLF: 'LibreWolf', LIEBAO: 'LBBROWSER', LINE: 'Line', LINKEDIN: 'LinkedIn', @@ -126,7 +128,9 @@ const Browser = Object.freeze({ SILK: 'Silk', SKYFIRE: 'Skyfire', SLEIPNIR: 'Sleipnir', + SLIMBOAT: 'SlimBoat', SLIMBROWSER: 'SlimBrowser', + SLIMJET: 'Slimjet', SNAPCHAT: 'Snapchat', SOGOU_EXPLORER: 'Sogou Explorer', SOGOU_MOBILE: 'Sogou Mobile', @@ -291,6 +295,10 @@ const OS = Object.freeze({ CENTOS: 'CentOS', CHROME_OS: 'Chrome OS', CHROMECAST: 'Chromecast', + CHROMECAST_ANDROID: 'Chromecast Android', + CHROMECAST_FUCHSIA: 'Chromecast Fuchsia', + CHROMECAST_LINUX: 'Chromecast Linux', + CHROMECAST_SMARTSPEAKER: 'Chromecast SmartSpeaker', CONTIKI: 'Contiki', DEBIAN: 'Debian', DEEPIN: 'Deepin', diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index e432b21f6..89f5bd3a5 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 124fe9923..182bd872d 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-rc.1 +/* Extensions for UAParser.js v2.0.0-rc.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index bf12ddf64..21c2dcb9a 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-rc.1 +/* Extensions for UAParser.js v2.0.0-rc.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 7074c229d..a1e3f20c5 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index c8f6925b7..7234e63d2 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-rc.1 +/* Helpers for UAParser.js v2.0.0-rc.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index 886d0dc31..7c6fd0eff 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-rc.1 +/* Helpers for UAParser.js v2.0.0-rc.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -11,9 +11,11 @@ /*jshint esversion: 6 */ -import { CPU, OS, Engine } from './enums/ua-parser-enums.mjs'; -import { UAParser } from './main/ua-parser.mjs'; +import { UAParser } from '../main/ua-parser.mjs'; +import { CPU, OS, Engine } from '../enums/ua-parser-enums.mjs'; import { isFromEU } from 'detect-europe-js'; +import { isFrozenUA } from 'ua-is-frozen'; +import { isStandalonePWA } from 'is-standalone-pwa'; const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; @@ -37,26 +39,17 @@ const isAppleSilicon = (res) => { return false; } +const isBot = (res) => ['cli', 'crawler', 'fetcher', 'module'].includes(res.browser.type); + const isChromeFamily = (res) => res.engine.is(Engine.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js / electron\//i.test(navigator?.userAgent)); // browser -const isFrozenUA = (ua) => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua); - -const isStandalonePWA = () => window && (window.matchMedia('(display-mode: standalone)').matches || - // iOS - navigator.standalone || - // Android - document.referrer.startsWith('android-app://') || - // Windows - window.Windows || - /trident.+(msapphost|webview)\//i.test(navigator.userAgent) || - document.referrer.startsWith('app-info://platform/microsoft-store')); - export { getDeviceVendor, isAppleSilicon, + isBot, isChromeFamily, isElectron, isFromEU, diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 03be97a52..b54f4cdb5 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0-beta.3 +// Type definitions for UAParser.js v2.0.0-rc.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 242f42a5c..797890213 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-rc.1 +/* UAParser.js v2.0.0-rc.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-rc.1', + var LIBVERSION = '2.0.0-rc.2', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index e9861fc5c..130f21649 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-rc.1 +/* UAParser.js v2.0.0-rc.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-rc.1', + var LIBVERSION = '2.0.0-rc.2', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -42,6 +42,7 @@ WEARABLE = 'wearable', XR = 'xr', EMBEDDED = 'embedded', + INAPP = 'inapp', USER_AGENT = 'user-agent', UA_MAX_LENGTH = 500, BRANDS = 'brands', @@ -322,17 +323,19 @@ // Mixed /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu ], [VERSION, [NAME, 'Baidu']], [ + /\b(?:mxbrowser|mxios|myie2)\/?([-\w\.]*)\b/i // Maxthon + ], [VERSION, [NAME, 'Maxthon']], [ /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir // Trident based - /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser + /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer - // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio - /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi + // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon + /(heytap|ovi|115)browser\/([\d\.]+)/i, // HeyTap/Ovi/115 /(weibo)__([\d\.]+)/i // Weibo ], [NAME, VERSION], [ /quark(?:pc)?\/([-\w\.]+)/i // Quark @@ -369,31 +372,31 @@ ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [ /fxios\/([\w\.-]+)/i // Firefox for iOS ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ - /\bqihu|(qi?ho?o?|360)browser/i // 360 - ], [[NAME, '360' + SUFFIX_BROWSER]], [ + /\bqihoobrowser\/?([\w\.]*)/i // 360 + ], [VERSION, [NAME, '360']], [ /\b(qq)\/([\w\.]+)/i // QQ ], [[NAME, /(.+)/, '$1Browser'], VERSION], [ /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser /samsungbrowser\/([\w\.]+)/i // Samsung Internet ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ - /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon - ], [[NAME, /_/g, ' '], VERSION], [ /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer ], [VERSION, [NAME, SOGOU + ' Explorer']], [ /(sogou)mo\w+\/([\d\.]+)/i // Sogou Mobile ], [[NAME, SOGOU + ' Mobile'], VERSION], [ /(electron)\/([\w\.]+) safari/i, // Electron-based App /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla - /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser + /m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i // QQ/2345 ], [NAME, VERSION], [ - /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq - /\[(linkedin)app\]/i // LinkedIn App for iOS & Android + /(lbbrowser|rekonq)/i // LieBao Browser/Rekonq ], [NAME], [ + /ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron + /ome\/([\w\.]+).+qihu (360)[es]e/i // 360 + ], [VERSION, NAME], [ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android - ], [[NAME, FACEBOOK], VERSION], [ + ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [ /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp @@ -401,12 +404,17 @@ /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter - /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat - ], [NAME, VERSION], [ + /(instagram|snapchat)[\/ ]([-\w\.]+)/i // Instagram/Snapchat + ], [NAME, VERSION, [TYPE, INAPP]], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS - ], [VERSION, [NAME, 'GSA']], [ + ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [ /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok - ], [VERSION, [NAME, 'TikTok']], [ + ], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [ + /\[(linkedin)app\]/i // LinkedIn App for iOS & Android + ], [NAME, [TYPE, INAPP]], [ + + /(chromium)[\/ ]([-\w\.]+)/i // Chromium + ], [NAME, VERSION], [ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ @@ -440,7 +448,7 @@ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ - /(wolvic)\/([\w\.]+)/i // Wolvic + /(wolvic|librewolf)\/([\w\.]+)/i // Wolvic/LibreWolf ], [NAME, VERSION], [ /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality ], [VERSION, [NAME, FIREFOX+' Reality']], [ From be2f167f9074ea633d7df34cb952721ee68e0e63 Mon Sep 17 00:00:00 2001 From: Aiyush Date: Sat, 9 Nov 2024 08:50:45 +0530 Subject: [PATCH 255/388] Added support for honor separated from Huawei (#749) * Added support for honor separated from Huawei * Revert "Added support for honor separated from Huawei" This reverts commit 28bde1c0b4da2aa14082a8d036914761e00ca750. * Added changes wrt comments * Added another change --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 7 ++++++- test/specs/device-all.json | 6 +++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 6d88e813f..85820f901 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -52,6 +52,7 @@ const DeviceVendor = Object.freeze({ APPLE : 'Apple', SAMSUNG : 'Samsung', HUAWEI : 'Huawei', + HONOR : 'Honor', XIAOMI : 'Xiaomi', OPPO : 'OPPO', VIVO : 'Vivo', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index c2b736d4a..5d4f80da2 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -68,6 +68,7 @@ BLACKBERRY = 'BlackBerry', GOOGLE = 'Google', HUAWEI = 'Huawei', + HONOR = 'Honor', LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', @@ -467,10 +468,14 @@ /\b(sh-?[altvz]?\d\d[a-ekm]?)/i ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ + // Honor + /(?:honor)([-\w ]+)[;\)]/i + ], [MODEL, [VENDOR, HONOR], [TYPE, MOBILE]], [ + // Huawei /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ - /(?:huawei|honor)([-\w ]+)[;\)]/i, + /(?:huawei)([-\w ]+)[;\)]/i, /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ diff --git a/test/specs/device-all.json b/test/specs/device-all.json index b43969c41..6f2dc51ce 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -632,7 +632,7 @@ "desc": "Huawei Honor 6A", "ua": "Mozilla/5.0 (Linux; Android 7.0; DLI-L22 Build/HONORDLI-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/252.0.0.22.355;]", "expect": { - "vendor": "Huawei", + "vendor": "Honor", "model": "DLI-L22", "type": "mobile" } @@ -641,7 +641,7 @@ "desc": "Huawei Honor 7", "ua": "Mozilla/5.0 (Linux; Android 6.0; PLK-L01 Build/HONORPLK-L01; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36", "expect": { - "vendor": "Huawei", + "vendor": "Honor", "model": "PLK-L01", "type": "mobile" } @@ -650,7 +650,7 @@ "desc": "Huawei 10 Lite", "ua": "Mozilla/5.0 (Linux; Android 9; HRY-LX1 Build/HONORHRY-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36", "expect": { - "vendor": "Huawei", + "vendor": "Honor", "model": "HRY-LX1", "type": "mobile" } From d5edcbae12ffe7025199d8839e2fb5c78cdb9e38 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 9 Nov 2024 10:21:36 +0700 Subject: [PATCH 256/388] Fix #763 - Add support for Headers object --- src/main/ua-parser.js | 9 ++++++++- test/playwright-test-main.spec.mjs | 19 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 797890213..8730b6397 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1236,14 +1236,21 @@ headers = extensions; // case UAParser(ua, headers) extensions = undefined; } + + // Convert Headers object into a plain object + if (headers && typeof headers.append === FUNC_TYPE) { + var kv = {}; + headers.forEach(function (v, k) { kv[k] = v; }); + headers = kv; + } if (!(this instanceof UAParser)) { return new UAParser(ua, extensions, headers).getResult(); } var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string - ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers + ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent EMPTY)), // empty string httpUACH = new UACHData(headers, true), diff --git a/test/playwright-test-main.spec.mjs b/test/playwright-test-main.spec.mjs index fe4ac7cbd..db6e149f1 100644 --- a/test/playwright-test-main.spec.mjs +++ b/test/playwright-test-main.spec.mjs @@ -123,4 +123,21 @@ test.describe('withFeatureCheck() tests', () => { expect(uap).toHaveProperty('device.model', 'iPad'); expect(uap).toHaveProperty('device.type', 'tablet'); }); -}); \ No newline at end of file +}); + +test.describe('request.headers can be passed in form of a Headers object', () => { + + test('Headers automatically converted into a plain key-value object', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(window, 'req', { + value : { + headers: new Headers([["User-Agent", "myBrowser/1.0"]]) + } + }); + }); + await page.goto(localHtml); + // @ts-ignore + const uap = await page.evaluate(() => UAParser(req.headers)); + expect(uap.ua).toBe('myBrowser/1.0'); + }); +}); From 19e5d322e27ced7e95abfa988218a0c28ef995b1 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 9 Nov 2024 20:04:29 +0700 Subject: [PATCH 257/388] [submodule:helpers] Add an optional parameter in `isAppleSilicon()` that flags the use of feature detection --- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index a1e3f20c5..0f46125a4 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -5,7 +5,7 @@ import { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; -declare function isAppleSilicon(res: IResult): boolean; +declare function isAppleSilicon(res: IResult, useFeatureDetection?: boolean): boolean; declare function isBot(res: IResult): boolean; declare function isChromeFamily(res: IResult): boolean; declare function isElectron(): boolean; diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 7234e63d2..ce2db0c8e 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -15,21 +15,23 @@ const { isStandalonePWA } = require('is-standalone-pwa'); const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isAppleSilicon = (res) => { +const isAppleSilicon = (res, useFeatureDetection) => { if (res.os.is(OS.MACOS)) { if (res.cpu.is(CPU.ARM)) { return true; } - try { - const canvas = document.createElement('canvas'); - const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); - const debug = webgl.getExtension('WEBGL_debug_renderer_info'); - const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL); - if (renderer.match(/apple m\d/i)) { - return true; + if (useFeatureDetection) { + try { + const canvas = document.createElement('canvas'); + const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + const debug = webgl.getExtension('WEBGL_debug_renderer_info'); + const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL); + if (renderer.match(/apple m\d/i)) { + return true; + } + } catch { + return false; } - } catch { - return false; } } return false; From 3b3361fe53bd74b4b3a58d5908cc7348742b3439 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 10 Nov 2024 08:15:39 +0700 Subject: [PATCH 258/388] [submodule:extensions] Breaking change: rename `module` to `library` --- src/enums/ua-parser-enums.js | 2 +- src/extensions/ua-parser-extensions.d.ts | 4 ++-- src/extensions/ua-parser-extensions.js | 18 +++++++++--------- src/helpers/ua-parser-helpers.js | 2 +- src/main/ua-parser.d.ts | 2 +- test/dts-test.ts | 2 +- test/mocha-test-extension.js | 14 +++++++------- ...ser-modules.json => browser-libraries.json} | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) rename test/specs/{browser-modules.json => browser-libraries.json} (86%) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 1c15464d1..903aa057c 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -159,7 +159,7 @@ const BrowserType = Object.freeze({ FETCHER: 'fetcher', INAPP: 'inapp', MEDIAPLAYER: 'mediaplayer', - MODULE: 'module' + LIBRARY: 'library' }); const CPU = Object.freeze({ diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 89f5bd3a5..013a3d75b 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -11,5 +11,5 @@ export const ExtraDevices: UAParserExt; export const Emails: UAParserExt; export const Fetchers: UAParserExt; export const InApps: UAParserExt; -export const MediaPlayers: UAParserExt; -export const Modules: UAParserExt; \ No newline at end of file +export const Libraries: UAParserExt; +export const MediaPlayers: UAParserExt; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 182bd872d..1ba574fad 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -20,7 +20,7 @@ const EMAIL = 'email'; const FETCHER = 'fetcher'; const INAPP = 'inapp'; const MEDIAPLAYER = 'mediaplayer'; -const MODULE = 'module'; +const LIBRARY = 'library'; ////////////////////// // COMMAND LINE APPS @@ -332,14 +332,14 @@ const MediaPlayers = Object.freeze({ ] }); -//////////////////////// -// MODULES / LIBRARIES -/////////////////////// +///////////// +// LIBRARIES +////////////// -const Modules = Object.freeze({ +const Libraries = Object.freeze({ browser : [ // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]] + [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] ] }); @@ -352,7 +352,7 @@ const Bots = Object.freeze({ ...CLIs.browser, ...Crawlers.browser, ...Fetchers.browser, - ...Modules.browser + ...Libraries.browser ] }); @@ -364,6 +364,6 @@ module.exports = { Emails, Fetchers, InApps, - MediaPlayers, - Modules + Libraries, + MediaPlayers }; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index ce2db0c8e..cd63a79ff 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -37,7 +37,7 @@ const isAppleSilicon = (res, useFeatureDetection) => { return false; } -const isBot = (res) => ['cli', 'crawler', 'fetcher', 'module'].includes(res.browser.type); +const isBot = (res) => ['cli', 'crawler', 'fetcher', 'library'].includes(res.browser.type); const isChromeFamily = (res) => res.engine.is(Engine.BLINK); diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index b54f4cdb5..cbf583717 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -15,7 +15,7 @@ declare namespace UAParser { name?: string; version?: string; major?: string; - type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'module'; + type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'library'; } interface ICPU extends IData { diff --git a/test/dts-test.ts b/test/dts-test.ts index 0f05a8aa2..39bb4a30f 100644 --- a/test/dts-test.ts +++ b/test/dts-test.ts @@ -28,7 +28,7 @@ expectType(browser); expectType(browser.name); expectType(browser.version); expectType(browser.major); -expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'module' | undefined>(browser.type); +expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'library' | undefined>(browser.type); expectType(browser.is('')); expectType(browser.toString()); expectType>(browser.withClientHints()); diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 2f81a5c2f..246bf35aa 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -8,8 +8,8 @@ const clis = require('./specs/browser-clis.json'); const crawlers = require('./specs/browser-crawlers.json'); const emails = require('./specs/browser-emails.json'); const fetchers = require('./specs/browser-fetchers.json'); -const modules = require('./specs/browser-modules.json'); -const { Bots, CLIs, Crawlers, Emails, Fetchers, Modules } = require('../src/extensions/ua-parser-extensions'); +const libraries = require('./specs/browser-libraries.json'); +const { Bots, CLIs, Crawlers, Emails, Fetchers, Libraries } = require('../src/extensions/ua-parser-extensions'); describe('Extensions', () => { [ @@ -17,7 +17,7 @@ describe('Extensions', () => { ['Crawlers', crawlers, Crawlers], ['Emails', emails, Emails], ['Fetchers', fetchers, Fetchers], - ['Modules', modules, Modules] + ['Libraries', libraries, Libraries] ] .forEach((list) => { describe(list[0], () => { @@ -44,10 +44,10 @@ describe('Extensions', () => { assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); - const moduleParser = new UAParser(Modules); - assert.deepEqual(moduleParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "module"}); - assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"}); - assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"}); + const libraryParser = new UAParser(Libraries); + assert.deepEqual(libraryParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "library"}); + assert.deepEqual(libraryParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "library"}); + assert.deepEqual(libraryParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "library"}); }); describe('Merge', () => { diff --git a/test/specs/browser-modules.json b/test/specs/browser-libraries.json similarity index 86% rename from test/specs/browser-modules.json rename to test/specs/browser-libraries.json index 1639a1973..8e0eb56fb 100644 --- a/test/specs/browser-modules.json +++ b/test/specs/browser-libraries.json @@ -6,7 +6,7 @@ { "name" : "Scrapy", "version" : "1.5.0", - "type" : "module" + "type" : "library" } } ] From a0e11b701e6c7b25230115a6de9e7d69dd3bfeee Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 10 Nov 2024 13:01:46 +0700 Subject: [PATCH 259/388] [submodule:extensions] Add some new bots: `360Spider`, `Archive.org Bots`, `CCBot`, `DataForSeoBot`, `DuckAssistBot`, `Exabot`, `Google Bots`, `Meta Bots`, `MojeekBot`, `PerplexityBot`, `PetalBot`, `TurnitinBot`, `Yeti`, `YisouSpider` --- src/extensions/ua-parser-extensions.js | 56 ++++- test/specs/browser-crawlers.json | 301 +++++++++++++++++++++++++ test/specs/browser-fetchers.json | 70 ++++++ 3 files changed, 415 insertions(+), 12 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 1ba574fad..d80e48c43 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -44,26 +44,36 @@ const Crawlers = Object.freeze({ // Amazonbot - https://developer.amazon.com/amazonbot // Applebot - http://apple.com/go/applebot // Bingbot - http://www.bing.com/bingbot.htm + // CCBot - https://commoncrawl.org/faq // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot // MJ12bot - https://mj12bot.com/ - // OpenAI Search - https://platform.openai.com/docs/bots + // MojeekBot - https://www.mojeek.com/bot.html + // OpenAI's SearchGPT - https://platform.openai.com/docs/bots + // PerplexityBot - https://perplexity.ai/perplexitybot // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|apple|bing|dot|duckduck|facebook|gpt|mj12|oai-search|semrush)bot)\/([\w\.]+)/i, + /((?:ahrefs|amazon|apple|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, - // ClaudeBot + // ClaudeBot (Anthropic) /(claude(?:bot|-web))\/([\w\.]+)/i, // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, + // Facebook / Meta + // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers + /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i, + // Googlebot - http://www.google.com/bot.html - /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i, + /(google(?:bot|other|-inspectiontool)(?:-image|-video|-news)?|storebot-google)\/?([\w\.]*)/i, + + // Internet Archive (archive.org) + /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, // Sogou Spider /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, @@ -72,14 +82,29 @@ const Crawlers = Object.freeze({ /(y!?j-(?:asr|br[uw]|dscv|mmp|vsidx|wsc))\/([\w\.]+)/i, // Yandex Bots - https://yandex.com/bots - /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i + /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i, + + // Yeti (Naver) + /(yeti)\/([\w\.]+)/i, + + // YisouSpider + /(yisouspider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], - // Bytespider - // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - [/((?:bytespider|(?=yahoo! )slurp))/i], + [ + // Google Bots + /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, + + // Bytespider + // DataForSeoBot - https://dataforseo.com/dataforseo-bot + // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot + // Qihoo 360Spider + // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + /(360spider-?(?:image|video)?|bytespider|(?:aspiegel|dataforseo|petal|turnitin)bot|(?=yahoo! )slurp)/i + ], [NAME, [TYPE, CRAWLER]] ] }); @@ -184,8 +209,15 @@ const Fetchers = Object.freeze({ [ // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit // ChatGPT-User - https://platform.openai.com/docs/plugins/bot + // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / Telegrambot / Twitterbot / UptimeRobot - /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot)\/([\w\.]+)/i, + /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot)\/([\w\.]+)/i, + + // Google Site Verifier + /(google-site-verification)\/([\w\.]+)/i, + + // Meta + /(meta-externalfetcher)\/([\w\.]+)/i, // Slackbot - https://api.slack.com/robots /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, @@ -203,7 +235,7 @@ const Fetchers = Object.freeze({ [NAME, VERSION, [TYPE, FETCHER]], // Google Bots / Snapchat - [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], + [/(feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], [NAME, [TYPE, FETCHER]], ] }); @@ -252,8 +284,8 @@ const MediaPlayers = Object.freeze({ /(flrp)\/([\w\.-]+)/i // Flip Player ], [[NAME, 'Flip Player'], VERSION, [TYPE, MEDIAPLAYER]], [ - /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i - // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit + /(fstream|nativehost|queryseekspider)/i + // FStream/NativeHost/QuerySeekSpider ], [NAME, [TYPE, MEDIAPLAYER]], [ /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json index 45e25ad18..e527740e3 100644 --- a/test/specs/browser-crawlers.json +++ b/test/specs/browser-crawlers.json @@ -1,4 +1,44 @@ [ + { + "desc" : "360Spider", + "ua" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0); 360Spider", + "expect" : + { + "name" : "360Spider", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "AdsBot Mobile Web", + "ua" : "AdsBot-Google (+http://www.google.com/adsbot.html)", + "expect" : + { + "name" : "AdsBot-Google", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "AdsBot Mobile Web", + "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; AdsBot-Google-Mobile; +http://www.google.com/mobile/adsbot.html)", + "expect" : + { + "name" : "AdsBot-Google-Mobile", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "AdSense", + "ua" : "Mediapartners-Google", + "expect" : + { + "name" : "Mediapartners-Google", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "AhrefsBot", "ua" : "Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)", @@ -49,6 +89,16 @@ "type" : "crawler" } }, + { + "desc" : "CCBot", + "ua" : "CCBot/1.0 (+https://commoncrawl.org/bot.html)", + "expect" : + { + "name" : "CCBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Coc Coc Bot (web)", "ua" : "Mozilla/5.0 (compatible; coccocbot-web/1.0; +http://help.coccoc.com/searchengine)", @@ -79,6 +129,16 @@ "type" : "crawler" } }, + { + "desc" : "DataForSEO", + "ua" : "Mozilla/5.0 (compatible; DataForSeoBot; +https://dataforseo.com/dataforseo-bot) ", + "expect" : + { + "name" : "DataForSeoBot", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Dotbot", "ua" : "Mozilla/5.0 (compatible; DotBot/1.2; +https://opensiteexplorer.org/dotbot; help@moz.com)", @@ -89,6 +149,16 @@ "type" : "crawler" } }, + { + "desc" : "Exabot", + "ua" : "Mozilla/5.0 (compatible; Exabot/3.0; +http://www.exabot.com/go/robot)", + "expect" : + { + "name" : "Exabot", + "version" : "3.0", + "type" : "crawler" + } + }, { "desc" : "FacebookBot", "ua" : "Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/", @@ -99,6 +169,26 @@ "type" : "crawler" } }, + { + "desc" : "FacebookExternalHit", + "ua" : "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)", + "expect" : + { + "name" : "facebookexternalhit", + "version" : "1.1", + "type" : "crawler" + } + }, + { + "desc" : "FacebookExternalHit", + "ua" : "facebookcatalog/1.0", + "expect" : + { + "name" : "facebookcatalog", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Googlebot-Video", "ua" : "Googlebot-Video/1.0", @@ -109,6 +199,106 @@ "type" : "crawler" } }, + { + "desc" : "Googlebot", + "ua" : "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", + "expect" : + { + "name" : "Googlebot", + "version" : "2.1", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot Image", + "ua" : "Googlebot-Image/1.0", + "expect" : + { + "name" : "Googlebot-Image", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot Video", + "ua" : "Googlebot-Video/1.0", + "expect" : + { + "name" : "Googlebot-Video", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot News", + "ua" : "Googlebot-News/1.0", + "expect" : + { + "name" : "Googlebot-News", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Google Storebot", + "ua" : "Storebot-Google/1.0", + "expect" : + { + "name" : "Storebot-Google", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Google InspectionTool", + "ua" : "Mozilla/5.0 (compatible; Google-InspectionTool/1.0;)", + "expect" : + { + "name" : "Google-InspectionTool", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "GoogleOther", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GoogleOther) Chrome/41.0.2272.96 Safari/537.36", + "expect" : + { + "name" : "GoogleOther", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "GoogleOther-Image", + "ua" : "GoogleOther-Image/1.0", + "expect" : + { + "name" : "GoogleOther-Image", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "GoogleOther-Video", + "ua" : "GoogleOther-Video/1.0", + "expect" : + { + "name" : "GoogleOther-Video", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Google-Safety", + "ua" : "Google-Safety", + "expect" : + { + "name" : "Google-Safety", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "GPTBot", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)", @@ -119,6 +309,36 @@ "type" : "crawler" } }, + { + "desc" : "Archive.org Bot", + "ua" : "ia_archiver/8.1 (Windows 2000 1.9; en-US;)", + "expect" : + { + "name" : "ia_archiver", + "version" : "8.1", + "type" : "crawler" + } + }, + { + "desc" : "Archive.org Bot", + "ua" : "Mozilla/5.0 (compatible; archive.org_bot/3.3.0 +https://archive.org/details/archive.org_bot)", + "expect" : + { + "name" : "archive.org_bot", + "version" : "3.3.0", + "type" : "crawler" + } + }, + { + "desc" : "Meta-ExternalAgent", + "ua" : "meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", + "expect" : + { + "name" : "meta-externalagent", + "version" : "1.1", + "type" : "crawler" + } + }, { "desc" : "MJ12bot", "ua" : "Mozilla/5.0 (compatible; MJ12bot/v1.4.8; http://mj12bot.com/)", @@ -126,6 +346,17 @@ { "name" : "MJ12bot", "version" : "v1.4.8", + "major" : "1", + "type" : "crawler" + } + }, + { + "desc" : "MojeekBot", + "ua" : "Mozilla/5.0 (compatible; MojeekBot/0.11; +https://www.mojeek.com/bot.html)", + "expect" : + { + "name" : "MojeekBot", + "version" : "0.11", "type" : "crawler" } }, @@ -139,6 +370,36 @@ "type" : "crawler" } }, + { + "desc" : "PerplexityBot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)", + "expect" : + { + "name" : "PerplexityBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "PetalBot", + "ua" : "Mozilla/5.0 (compatible;PetalBot; +https://webmaster.petalsearch.com/site/petalbot) ", + "expect" : + { + "name" : "PetalBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "PetalBot", + "ua" : "Mozilla/5.0 (Linux; Android 7.0;) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; PetalBot;+https://webmaster.petalsearch.com/site/petalbot) ", + "expect" : + { + "name" : "PetalBot", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "SemrushBot", "ua" : "Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)", @@ -149,6 +410,16 @@ "type" : "crawler" } }, + { + "desc" : "TurnitinBot", + "ua" : "TurnitinBot (https://turnitin.com/robot/crawlerinfo.html)", + "expect" : + { + "name" : "TurnitinBot", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Yahoo! Japan", "ua" : "Y!J-BRW/1.0 (https://www.yahoo-help.jp/app/answers/detail/p/595/a_id/42716)", @@ -168,5 +439,35 @@ "version" : "3.0", "type" : "crawler" } + }, + { + "desc" : "Yeti", + "ua" : "Mozilla/5.0 (compatible; Yeti/1.1; +http://naver.me/spd)", + "expect" : + { + "name" : "Yeti", + "version" : "1.1", + "type" : "crawler" + } + }, + { + "desc" : "YisouSpider", + "ua" : "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 YisouSpider/5.0 Safari/537.36", + "expect" : + { + "name" : "YisouSpider", + "version" : "5.0", + "type" : "crawler" + } + }, + { + "desc" : "YisouSpider", + "ua" : "YisouSpider", + "expect" : + { + "name" : "YisouSpider", + "version" : "undefined", + "type" : "crawler" + } } ] diff --git a/test/specs/browser-fetchers.json b/test/specs/browser-fetchers.json index c508bd22f..94bada00e 100644 --- a/test/specs/browser-fetchers.json +++ b/test/specs/browser-fetchers.json @@ -29,6 +29,76 @@ "type" : "fetcher" } }, + { + "desc" : "DuckAssistBot", + "ua" : "DuckAssistBot/1.2; (+http://duckduckgo.com/duckassistbot.html)", + "expect" : + { + "name" : "DuckAssistBot", + "version" : "1.2", + "type" : "fetcher" + } + }, + { + "desc" : "Google FeedFetcher", + "ua" : "FeedFetcher-Google; (+http://www.google.com/feedfetcher.html)", + "expect" : + { + "name" : "FeedFetcher-Google", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Read Aloud - Mobile agent", + "ua" : "Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36 (compatible; Google-Read-Aloud; +https://support.google.com/webmasters/answer/1061943)", + "expect" : + { + "name" : "Google-Read-Aloud", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Read Aloud - Desktop agent", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36 (compatible; Google-Read-Aloud; +https://support.google.com/webmasters/answer/1061943)", + "expect" : + { + "name" : "Google-Read-Aloud", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Publisher Center", + "ua" : "GoogleProducer; (+https://developers.google.com/search/docs/crawling-indexing/google-producer)", + "expect" : + { + "name" : "GoogleProducer", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Site Verifier", + "ua" : "Mozilla/5.0 (compatible; Google-Site-Verification/1.0)", + "expect" : + { + "name" : "Google-Site-Verification", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Meta-ExternalFetcher", + "ua" : "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", + "expect" : + { + "name" : "meta-externalfetcher", + "version" : "1.1", + "type" : "fetcher" + } + }, { "desc" : "Rogerbot", "ua" : "Mozilla/5.0 (compatible; rogerBot/1.0; UrlCrawler; http://www.seomoz.org/dp/rogerbot)", From cbe06acaf2834d897c2d3243ad86a81bd2abaa4f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 10 Nov 2024 19:32:37 +0700 Subject: [PATCH 260/388] [submodule:extensions] Add email clients: `Evolution`, `KMail`, `Kontact` --- src/extensions/ua-parser-extensions.js | 4 +- test/specs/browser-emails.json | 60 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index d80e48c43..936c90bd2 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -195,8 +195,8 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ - // Microsoft Outlook / Thunderbird - [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] + // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird + [/(evolution|kmail2?|kontact|(?:microsoft |mac)outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] ] }); diff --git a/test/specs/browser-emails.json b/test/specs/browser-emails.json index 1ce6b3029..883f32be6 100644 --- a/test/specs/browser-emails.json +++ b/test/specs/browser-emails.json @@ -1,4 +1,64 @@ [ + { + "desc" : "Evolution", + "ua" : "Evolution/3.52.3", + "expect" : + { + "name" : "Evolution", + "version" : "3.52.3", + "type" : "email" + } + }, + { + "desc" : "KMail", + "ua" : "KMail/4.14.10 (FreeBSD/12.0-CURRENT; KDE/4.14.10; amd64; ; )", + "expect" : + { + "name" : "KMail", + "version" : "4.14.10", + "type" : "email" + } + }, + { + "desc" : "KMail2", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) kmail2/5.7.3 Safari/534.34", + "expect" : + { + "name" : "kmail2", + "version" : "5.7.3", + "type" : "email" + } + }, + { + "desc" : "Kontact", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) kontact/4.13.3 Safari/534.34", + "expect" : + { + "name" : "kontact", + "version" : "4.13.3", + "type" : "email" + } + }, + { + "desc" : "Microsoft Outlook", + "ua" : "Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.5431; Pro)", + "expect" : + { + "name" : "Microsoft Outlook", + "version" : "16.0.5431", + "type" : "email" + } + }, + { + "desc" : "Microsoft Outlook for Mac", + "ua" : "MacOutlook/14.7.1.161129 (Intel Mac OS X 10.9.6)", + "expect" : + { + "name" : "MacOutlook", + "version" : "14.7.1.161129", + "type" : "email" + } + }, { "desc" : "Thunderbird", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0", From 49151e478c4af104d7226463c5c4d3ff641fdbea Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 11 Nov 2024 19:55:40 +0700 Subject: [PATCH 261/388] Add new browser engine: `Servo` --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 2 +- test/specs/engine-all.json | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 903aa057c..c81194f90 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -274,6 +274,7 @@ const Engine = Object.freeze({ NETFRONT: 'NetFront', NETSURF: 'NetSurf', PRESTO: 'Presto', + SERVO: 'Servo', TASMAN: 'Tasman', TRIDENT: 'Trident', W3M: 'w3m', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7d5173540..0f046afaf 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -814,7 +814,7 @@ ], [VERSION, [NAME, 'Blink']], [ /(presto)\/([\w\.]+)/i, // Presto - /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna + /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna/Servo /ekioh(flow)\/([\w\.]+)/i, // Flow /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab diff --git a/test/specs/engine-all.json b/test/specs/engine-all.json index 67fdd3381..7523fc36e 100644 --- a/test/specs/engine-all.json +++ b/test/specs/engine-all.json @@ -89,6 +89,15 @@ "version" : "2.8.149" } }, + { + "desc" : "Servo", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Servo/1.0 Firefox/111.0", + "expect" : + { + "name" : "Servo", + "version" : "1.0" + } + }, { "desc" : "Tasman", "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; PPC Mac OS X 10.4.7; Tasman 1.0)", From 6b8920c7007d92ade3d7468062c274b81f00cbb0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 11 Nov 2024 21:41:07 +0700 Subject: [PATCH 262/388] Add new device vendors: `Cat`, `Energizer`, `Micromax` - https://www.catphones.com - https://www.energizeyourdevice.com - https://micromaxinfo.com/ --- src/enums/ua-parser-enums.js | 3 ++ src/main/ua-parser.js | 16 +++++-- test/specs/device-all.json | 90 ++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index c81194f90..67d0155a2 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -203,7 +203,9 @@ const Vendor = Object.freeze({ ATT: 'AT&T', BENQ: 'BenQ', BLACKBERRY: 'BlackBerry', + CAT: 'Cat', DELL: 'Dell', + ENERGIZER: 'Energizer', ESSENTIAL: 'Essential', FACEBOOK: 'Facebook', FAIRPHONE: 'Fairphone', @@ -220,6 +222,7 @@ const Vendor = Object.freeze({ LENOVO: 'Lenovo', LG: 'LG', MEIZU: 'Meizu', + MICROMAX: 'Micromax', MICROSOFT: 'Microsoft', MOTOROLA: 'Motorola', NEXIAN: 'Nexian', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0f046afaf..6c7a86ea5 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -600,7 +600,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ // Sony - /droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i + /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [ /sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i @@ -666,13 +666,23 @@ /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [ + // Energizer + /; (energy ?\w+)(?: bui|\))/i, + /; energizer ([\w ]+?)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Energizer'], [TYPE, MOBILE]], [ + + // Cat + /; cat (b35);/i, + /; (b15q?|s22 flip|s48c|s62 pro)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Cat'], [TYPE, MOBILE]], [ + // Nothing /droid.+; (a(?:015|06[35]|142p?))/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // MIXED - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i, - // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno|micromax)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax /(hp) ([\w ]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i, // Asus /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia diff --git a/test/specs/device-all.json b/test/specs/device-all.json index e7003247c..810a15374 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -215,6 +215,42 @@ "type": "mobile" } }, + { + "desc": "Cat B15Q", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; B15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "Cat", + "model": "B15Q", + "type": "mobile" + } + }, + { + "desc": "Cat B35", + "ua": "Mozilla/5.0 (Mobile; CAT B35; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5.1", + "expect": { + "vendor": "Cat", + "model": "B35", + "type": "mobile" + } + }, + { + "desc": "Cat S22 Flip", + "ua": "Mozilla/5.0 (Linux; Android 11; S22 FLIP Build/RKQ1.210416.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.165 Mobile Safari/537.36", + "expect": { + "vendor": "Cat", + "model": "S22 FLIP", + "type": "mobile" + } + }, + { + "desc": "Cat S62 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; S62 Pro Build/RKQ1.210406.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/94.0.4606.85 Mobile Safari/537.36 GSA/12.34.17.23.arm64", + "expect": { + "vendor": "Cat", + "model": "S62 Pro", + "type": "mobile" + } + }, { "desc": "Desktop (IE11 with Tablet string)", "ua": "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; GWX:MANAGED; rv:11.0) like Gecko", @@ -233,6 +269,33 @@ "type": "mobile" } }, + { + "desc": "Energizer Energy 400", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Energy400 Build/MRA58K test-keys; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.158 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/172.0.0.66.93;]", + "expect": { + "vendor": "Energizer", + "model": "Energy400", + "type": "mobile" + } + }, + { + "desc": "Energizer Energy 400S", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Energy 400S Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.85 Mobile Safari/537.36", + "expect": { + "vendor": "Energizer", + "model": "Energy 400S", + "type": "mobile" + } + }, + { + "desc": "Energizer Ultimate 65G", + "ua": "Mozilla/5.0 (Linux; Android 14; Energizer Ultimate 65G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Energizer", + "model": "Ultimate 65G", + "type": "mobile" + } + }, { "desc": "Fairphone 1U", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; FP1U Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", @@ -1151,6 +1214,33 @@ "type": "mobile" } }, + { + "desc": "Micromax Bharat 2 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; en-US; Micromax Q402Plus Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.12.9.1226 Mobile Safari/537.36", + "expect": { + "vendor": "Micromax", + "model": "Q402Plus", + "type": "mobile" + } + }, + { + "desc": "Micromax Canvas Infinity", + "ua": "Mozilla/5.0 (Linux; U; Android 7.1.2; en-US; Micromax HS2 Build/N2G47H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/13.2.0.1296 (SpeedMode) U4/1.0 UCWEB/2.0 Mobile Safari/534.30", + "expect": { + "vendor": "Micromax", + "model": "HS2", + "type": "mobile" + } + }, + { + "desc": "Micromax In 1b", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Micromax E7533 Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 OPR/54.0.2254.56148", + "expect": { + "vendor": "Micromax", + "model": "E7533", + "type": "mobile" + } + }, { "desc": "Microsoft Lumia 950", "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", From 84c46131454954c5b767cb8b8259645b5b3e39af Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 12 Nov 2024 16:07:06 +0700 Subject: [PATCH 263/388] Add new device vendors: `Advan`, `IMO`, `Smartfren` --- src/enums/ua-parser-enums.js | 3 + src/main/ua-parser.js | 12 +++- test/specs/device-all.json | 126 +++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 3 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 67d0155a2..74218fc14 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -195,6 +195,7 @@ const Device = Object.freeze({ const Vendor = Object.freeze({ ACER: 'Acer', + ADVAN: 'Advan', ALCATEL: 'Alcatel', APPLE: 'Apple', AMAZON: 'Amazon', @@ -215,6 +216,7 @@ const Vendor = Object.freeze({ HP: 'HP', HTC: 'HTC', HUAWEI: 'Huawei', + IMO: 'IMO', INFINIX: 'Infinix', ITEL: 'itel', JOLLA: 'Jolla', @@ -244,6 +246,7 @@ const Vendor = Object.freeze({ SAMSUNG: 'Samsung', SHARP: 'Sharp', SIEMENS: 'Siemens', + SMARTFREN: 'Smartfren', SONY: 'Sony', SPRINT: 'Sprint', TCL: 'TCL', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 6c7a86ea5..3615efd46 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -668,7 +668,7 @@ // Energizer /; (energy ?\w+)(?: bui|\))/i, - /; energizer ([\w ]+?)(?: bui|\))/i + /; energizer ([\w ]+)(?: bui|\))/i ], [MODEL, [VENDOR, 'Energizer'], [TYPE, MOBILE]], [ // Cat @@ -676,13 +676,18 @@ /; (b15q?|s22 flip|s48c|s62 pro)(?: bui|\))/i ], [MODEL, [VENDOR, 'Cat'], [TYPE, MOBILE]], [ + // Smartfren + /((?:new )?andromax[\w- ]+)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [ + // Nothing /droid.+; (a(?:015|06[35]|142p?))/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // MIXED - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno|micromax)[-_ ]?([-\w]*)/i, - // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan + /; (imo) ((?!tab)[\w ]+?)(?: bui|\))/i, // IMO /(hp) ([\w ]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i, // Asus /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia @@ -691,6 +696,7 @@ /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + /(imo) (tab \w+)/i, // IMO /(kobo)\s(ereader|touch)/i, // Kobo /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 810a15374..f0898b9f1 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -8,6 +8,33 @@ "type": "mobile" } }, + { + "desc": "Advan M4", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; ADVAN M4 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36 OPR/28.0.2254.119214", + "expect": { + "vendor": "ADVAN", + "model": "M4", + "type": "mobile" + } + }, + { + "desc": "Advan S40", + "ua": "Mozilla/5.0 (Linux; Android 7.0; ADVAN S40 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Mobile Safari/537.36 EdgA/79.0.309.58", + "expect": { + "vendor": "ADVAN", + "model": "S40", + "type": "mobile" + } + }, + { + "desc": "Advan Sketsa 2", + "ua": "Mozilla/5.0 (Linux; Android 11; ADVAN 1011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Safari/537.36", + "expect": { + "vendor": "ADVAN", + "model": "1011", + "type": "mobile" + } + }, { "desc": "ASUS Nexus 7", "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", @@ -845,6 +872,42 @@ "type": "mobile" } }, + { + "desc": "IMO FEEL A2", + "ua": "Mozilla/5.0 (Linux; Android 5.1; IMO FEEL A2 Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/49.0.2623.105 Mobile Safari/537.36", + "expect": { + "vendor": "IMO", + "model": "FEEL A2", + "type": "mobile" + } + }, + { + "desc": "IMO Q2", + "ua": "Mozilla/5.0 (Linux; Android 5.1; IMO Q2 Build/LMY47D; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Mobile Safari/537.36 GSA/7.22.24.21.arm", + "expect": { + "vendor": "IMO", + "model": "Q2", + "type": "mobile" + } + }, + { + "desc": "IMO S2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; IMO S2 Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.162 Mobile Safari/537.36", + "expect": { + "vendor": "IMO", + "model": "S2", + "type": "mobile" + } + }, + { + "desc": "IMO Tab X9", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; id-id; IMO TAB X9 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "IMO", + "model": "TAB X9", + "type": "tablet" + } + }, { "desc": "Infinix Hot 7 Pro", "ua": "Mozilla/5.0 (Linux; Android 9; Infinix X625C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", @@ -1997,6 +2060,42 @@ "type": "xr" } }, + { + "desc": "Polytron Prime 7 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; POLYTRON_P552 Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/64.0.3282.137 Mobile Safari/537.36 OPR/50.0.2254.149182", + "expect": { + "vendor": "POLYTRON", + "model": "P552", + "type": "mobile" + } + }, + { + "desc": "Polytron Rocket T1", + "ua": "Mozilla/5.0 (Linux; U; Android 5.0; en-US; POLYTRON R2501 Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/13.1.2.1293 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "R2501", + "type": "mobile" + } + }, + { + "desc": "Polytron Rocket T6", + "ua": "Mozilla/5.0 (Linux; Android 7.0; POLYTRON R2509) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.92 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "R2509", + "type": "mobile" + } + }, + { + "desc": "Polytron Zap 6 Posh", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1; in-ID; POLYTRON_4G501 Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.0.1163 UCTurbo/1.9.9.900 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "4G501", + "type": "mobile" + } + }, { "desc": "Roku", "ua": "Mozilla/5.0 (Roku) AppleWebKit/537.36 (KHTML, like Gecko) Web/1.1 Safari/537.36", @@ -2447,6 +2546,33 @@ "type": "mobile" } }, + { + "desc": "Smartfren Andromax L", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Andromax B26D2H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Smartfren", + "model": "Andromax B26D2H", + "type": "mobile" + } + }, + { + "desc": "Smartfren Andromax G2", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Smartfren Andromax AD9A1H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.83 Mobile Safari/537.36", + "expect": { + "vendor": "Smartfren", + "model": "Andromax AD9A1H", + "type": "mobile" + } + }, + { + "desc": "Smartfren New Andromax I", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; id-id; New Andromax-i Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Smartfren", + "model": "New Andromax-i", + "type": "mobile" + } + }, { "desc": "SONY Xperia 1 III", "ua": "Mozilla/5.0 (Linux; Android 11; A101SO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36", From ee51caf422b157bb93e88cc091c4aa403df437f2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 13 Nov 2024 10:44:38 +0700 Subject: [PATCH 264/388] Update version to `2.0.0-rc.3` --- CHANGELOG.md | 39 +++++++----- README.md | 62 ++++++++++-------- dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- package-lock.json | 28 ++++----- package.json | 8 +-- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 12 +++- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 80 +++++++++++++++++------- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 26 ++++---- src/main/ua-parser.d.ts | 6 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 44 ++++++++++--- 17 files changed, 208 insertions(+), 119 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52575f009..3a16881fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # UAParser.js Changelog +# Version 2.0 ## Migrating from v1 to v2 - What's breaking: @@ -8,28 +9,38 @@ - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` - What's new: - New device type: `xr`, to identify AR/VR devices - - New browser property: `browser.type`, to identify the type of the browser: `crawler`, `cli`, `email`, `fetcher`, `inapp`, `mediaplayer`, `module` + - New browser property: `browser.type`, to identify the type of the browser: `crawler`, `cli`, `email`, `fetcher`, `inapp`, `library`, `mediaplayer` - New methods in result object (all of `get*()` return value): - - Support for client hints: `withClientHints()` - - Support for feature detection: `withFeatureCheck()` + - Enhance detection using client hints: `withClientHints()` + - Enhance detection using feature check: `withFeatureCheck()` - Utility for easy comparison: `is()` - Utility to print full-name: `toString()` - - Parse directly from command line using `npx ua-parser-js` + - Parse user-agent directly from command line using `npx ua-parser-js "[User-Agent]"` - Extensions can be passed as a list to `UAParser()` - Support for ES module & TypeScript `import { UAParser } from 'ua-parser-js'` - Provided Enums submodule `'ua-parser-js/enums'` - Provided Extensions submodule `'ua-parser-js/extensions'` - Provided Helpers submodule `'ua-parser-js/helpers'`: - - `getDeviceVendor()` to guess for a device vendor based on its model name - - `isAppleSilicon()` to check if the device has Apple Silicon Mac device properties - - `isBot()` to check if the browser is identified as a bot - - `isChromeFamily()` to check if the browser is Chrome-based / has Blink engine (i.e: New Opera, New Edge, Vivaldi, Brave, Arc, etc.) - - `isElectron()` to check if current window is running inside Electron - - `isFromEU()` to check if current window is from an EU (European Union) country - - `isFrozenUA()` to check if a user-agent string match with the reduced/frozen user-agent pattern - - `isStandalonePWA()` to check if current window is a standalone PWA + - `getDeviceVendor()`: guess for a device vendor based on its model name + - `isAppleSilicon()`: check if the device has Apple Silicon Mac device properties + - `isBot()`: check if the browser is identified as a bot + - `isChromeFamily()`: check if the browser is Chrome-based (has Blink engine, i.e: New Opera, New Edge, Vivaldi, Brave, Arc, etc.) + - `isElectron()`: check if current window is running inside Electron + - `isFromEU()`: check if current window is from an EU (European Union) country + - `isFrozenUA()`: check if a user-agent string match with the reduced/frozen user-agent pattern + - `isStandalonePWA()`: check if current window is a standalone PWA + +--- --- +## Version 2.0.0-rc.3 + +- Add support for Headers object +- Add new device: Advan, Cat, Energizer, Honor, IMO, Micromax, Smartfren +- Add new engine: Servo +- `ua-parser-js/extensions` submodule: + - Breaking change: rename `module` to `library` + - Add new email clients: Evolution, KMail, Kontact + - Add new bots: 360Spider, Archive.org Bots, CCBot, DataForSeoBot, DuckAssistBot, Exabot, Google Bots, Meta Bots, MojeekBot, PerplexityBot, PetalBot, TurnitinBot, Yeti, YisouSpider ## Version 2.0.0-rc.2 @@ -59,7 +70,7 @@ - Rename `isChromiumBased()` to `isChromeFamily()` - Update `isAppleSilicon()` to also checks for WebGL renderer info - `ua-parser-js/extensions` submodule: - - Restore `bots` as a compilation of all these browser types: `clis`, `crawlers`, `fetchers`, and `modules` + - Restore `Bots` as a compilation of all these browser types: `cli`, `crawler`, `fetcher`, and `library` ## Version 2.0.0-beta.3 diff --git a/README.md b/README.md index 701add0cd..7b0aa5fa8 100644 --- a/README.md +++ b/README.md @@ -44,15 +44,15 @@ see what's new & breaking. License options - MIT (v1.x) - AGPL (v2.x) + MIT (v0.7~v1.0) + AGPL (>=v2.0) PRO Personal PRO Business PRO Enterprise Browser detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -60,7 +60,7 @@ see what's new & breaking. CPU detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -68,7 +68,7 @@ see what's new & breaking. Device detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -76,7 +76,7 @@ see what's new & breaking. Engine detection - ⚠️ + ⚠️ ✅ ✅ ✅ @@ -84,31 +84,39 @@ see what's new & breaking. OS detection - ⚠️ + ⚠️ ✅ ✅ ✅ ✅ - Enhanced detection - ⛔️ + Bot detection + ❌ ✅ ✅ ✅ ✅ - Client Hints support - ⛔️ + Extras (Apps, Libs, Emails, Media Players, etc) + ❌ + ✅ + ✅ + ✅ + ✅ + + + Enhanced detection result + ❌ ✅ ✅ ✅ ✅ - Extras (Apps, Bots, Libs, Emails, Media Players, etc) - ⛔️ + Client Hints support + ❌ ✅ ✅ ✅ @@ -124,14 +132,14 @@ see what's new & breaking. ES modules support - ⛔️ + ❌ ✅ ✅ ✅ ✅ - npm module available + npm module ✅ ✅ ✅ @@ -139,25 +147,25 @@ see what's new & breaking. ✅ - TypeScript declarations available - ⚠️ + TypeScript declarations + ⚠️ ✅ ✅ ✅ ✅ - Allowed for commercial use + Allows commercial use ✅ ✅ - ⛔️ + ❌ ✅ ✅ Permissive (non-copyleft) license ✅ - ⛔️ + ✅ ✅ ✅ @@ -167,13 +175,13 @@ see what's new & breaking. ✅ ✅ ✅ - ⛔️ + ✅ - 1-year support - ⛔️ - ⛔️ + 1-year product support + ❌ + ❌ ✅ ✅ ✅ @@ -192,13 +200,13 @@ see what's new & breaking. FREE (License) $14 (License) $29 (License) - $588 (License) + $599 (License) - GET THE PRO PACKAGES 📥 +

GET THE PRO PACKAGES 📥

@@ -221,4 +229,4 @@ Made with [contributors-img](https://contrib.rocks). -Support the open-source versions of UAParser.js on [OpenCollective](https://opencollective.com/ua-parser-js) or [GitHub Sponsors](https://github.com/sponsors/faisalman). +Support the open-source editions of UAParser.js on [OpenCollective](https://opencollective.com/ua-parser-js) or [GitHub Sponsors](https://github.com/sponsors/faisalman). diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 18512030c..bfc76aa6f 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-rc.2 +/* UAParser.js v2.0.0-rc.3 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-rc.2",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:headers&&headers[USER_AGENT]?headers[USER_AGENT]:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0-rc.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 0f145e7e3..55b1df175 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-rc.2 +/* UAParser.js v2.0.0-rc.3 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -!function(i,c){"use strict";function e(i){for(var e={},t=0;tT?Ti(i,T):i),this}]]).setUA(o),this}Ui.VERSION="2.0.0-rc.2",Ui.BROWSER=e([m,v,h,f]),Ui.CPU=e([k]),Ui.DEVICE=e([p,g,f,x,y,t,r,o,d]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===l&&define.amd?define(function(){return Ui}):ui&&(i.UAParser=Ui);var ji,Mi=ui&&(i.jQuery||i.Zepto);Mi&&!Mi.ua&&(ji=new Ui,Mi.ua=ji.getResult(),Mi.ua.get=function(){return ji.getUA()},Mi.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Mi.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,d){"use strict";function e(i){for(var e={},t=0;tC?Ci(i,C):i),this}]]).setUA(r),this}Ui.VERSION="2.0.0-rc.3",Ui.BROWSER=e([m,v,p,f]),Ui.CPU=e([k]),Ui.DEVICE=e([h,g,f,x,y,t,r,o,s]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):li&&(i.UAParser=Ui);var ji,Ei=li&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(ji=new Ui,Ei.ua=ji.getResult(),Ei.ua.get=function(){return ji.getUA()},Ei.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8d8b2bc24..42acf6be8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-rc.2", + "version": "2.0.0-rc.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-rc.2", + "version": "2.0.0-rc.3", "funding": [ { "type": "opencollective", @@ -23,9 +23,9 @@ ], "license": "AGPL-3.0-or-later", "dependencies": { - "detect-europe-js": "^0.1.1", - "is-standalone-pwa": "^0.1.0", - "ua-is-frozen": "^0.1.1" + "detect-europe-js": "^0.1.2", + "is-standalone-pwa": "^0.1.1", + "ua-is-frozen": "^0.1.2" }, "bin": { "ua-parser-js": "script/cli.js" @@ -1670,9 +1670,9 @@ "dev": true }, "node_modules/detect-europe-js": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.1.tgz", - "integrity": "sha512-+bUXDf+tI3L4dcEuRdAFa44Amx9aEaJzoZssx7Xis4H1bXWc5fAcOP850BOj0wJPRzOdovOuOVEvrg6T+GflZA==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.2.tgz", + "integrity": "sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==", "funding": [ { "type": "github", @@ -2612,9 +2612,9 @@ } }, "node_modules/is-standalone-pwa": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-standalone-pwa/-/is-standalone-pwa-0.1.0.tgz", - "integrity": "sha512-n5SQqXd0/JEkrKYEB7ZUndwuS7NKskZvk6rZZt6kTE1jiPxtPfPvVhXkfteIKpUfcEP07qsja/Wjz9NDjiZ5gg==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-standalone-pwa/-/is-standalone-pwa-0.1.1.tgz", + "integrity": "sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==", "funding": [ { "type": "github", @@ -4619,9 +4619,9 @@ } }, "node_modules/ua-is-frozen": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ua-is-frozen/-/ua-is-frozen-0.1.1.tgz", - "integrity": "sha512-TxhyfblPzcDJXRXu/j+73OI6s1jG6PUZBF/8hjTHoAsjZYKl9IhZzLQlnZHFLe5U2mvL1lMOOmcy647KpUq25A==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ua-is-frozen/-/ua-is-frozen-0.1.2.tgz", + "integrity": "sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==", "funding": [ { "type": "github", diff --git a/package.json b/package.json index 1beaf501a..73f74c852 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-rc.2", + "version": "2.0.0-rc.3", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ @@ -211,9 +211,9 @@ "test:playwright": "playwright test" }, "dependencies": { - "detect-europe-js": "^0.1.1", - "is-standalone-pwa": "^0.1.0", - "ua-is-frozen": "^0.1.1" + "detect-europe-js": "^0.1.2", + "is-standalone-pwa": "^0.1.1", + "ua-is-frozen": "^0.1.2" }, "devDependencies": { "@babel/parser": "7.15.8", diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 74218fc14..ff0156d90 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-rc.2 +/* Enums for UAParser.js v2.0.0-rc.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 6af7ef755..dff46cb72 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-rc.2 +/* Enums for UAParser.js v2.0.0-rc.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -59,6 +59,7 @@ const Browser = Object.freeze({ GOOGLE_SEARCH: 'GSA', HELIO: 'Helio', HEYTAP: 'HeyTap', + HONOR: 'Honor', HUAWEI: 'Huawei Browser', ICAB: 'iCab', ICE: 'ICE Browser', @@ -162,7 +163,7 @@ const BrowserType = Object.freeze({ FETCHER: 'fetcher', INAPP: 'inapp', MEDIAPLAYER: 'mediaplayer', - MODULE: 'module' + LIBRARY: 'library' }); const CPU = Object.freeze({ @@ -198,6 +199,7 @@ const Device = Object.freeze({ const Vendor = Object.freeze({ ACER: 'Acer', + ADVAN: 'Advan', ALCATEL: 'Alcatel', APPLE: 'Apple', AMAZON: 'Amazon', @@ -206,7 +208,9 @@ const Vendor = Object.freeze({ ATT: 'AT&T', BENQ: 'BenQ', BLACKBERRY: 'BlackBerry', + CAT: 'Cat', DELL: 'Dell', + ENERGIZER: 'Energizer', ESSENTIAL: 'Essential', FACEBOOK: 'Facebook', FAIRPHONE: 'Fairphone', @@ -216,6 +220,7 @@ const Vendor = Object.freeze({ HP: 'HP', HTC: 'HTC', HUAWEI: 'Huawei', + IMO: 'IMO', INFINIX: 'Infinix', ITEL: 'itel', JOLLA: 'Jolla', @@ -223,6 +228,7 @@ const Vendor = Object.freeze({ LENOVO: 'Lenovo', LG: 'LG', MEIZU: 'Meizu', + MICROMAX: 'Micromax', MICROSOFT: 'Microsoft', MOTOROLA: 'Motorola', NEXIAN: 'Nexian', @@ -244,6 +250,7 @@ const Vendor = Object.freeze({ SAMSUNG: 'Samsung', SHARP: 'Sharp', SIEMENS: 'Siemens', + SMARTFREN: 'Smartfren', SONY: 'Sony', SPRINT: 'Sprint', TCL: 'TCL', @@ -277,6 +284,7 @@ const Engine = Object.freeze({ NETFRONT: 'NetFront', NETSURF: 'NetSurf', PRESTO: 'Presto', + SERVO: 'Servo', TASMAN: 'Tasman', TRIDENT: 'Trident', W3M: 'w3m', diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 013a3d75b..b4da5499b 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.2 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 936c90bd2..a6774ad8b 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-rc.2 +/* Extensions for UAParser.js v2.0.0-rc.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 21c2dcb9a..5fc374d5d 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-rc.2 +/* Extensions for UAParser.js v2.0.0-rc.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -24,7 +24,7 @@ const EMAIL = 'email'; const FETCHER = 'fetcher'; const INAPP = 'inapp'; const MEDIAPLAYER = 'mediaplayer'; -const MODULE = 'module'; +const LIBRARY = 'library'; ////////////////////// // COMMAND LINE APPS @@ -48,26 +48,36 @@ const Crawlers = Object.freeze({ // Amazonbot - https://developer.amazon.com/amazonbot // Applebot - http://apple.com/go/applebot // Bingbot - http://www.bing.com/bingbot.htm + // CCBot - https://commoncrawl.org/faq // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot // MJ12bot - https://mj12bot.com/ - // OpenAI Search - https://platform.openai.com/docs/bots + // MojeekBot - https://www.mojeek.com/bot.html + // OpenAI's SearchGPT - https://platform.openai.com/docs/bots + // PerplexityBot - https://perplexity.ai/perplexitybot // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|apple|bing|dot|duckduck|facebook|gpt|mj12|oai-search|semrush)bot)\/([\w\.]+)/i, + /((?:ahrefs|amazon|apple|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, - // ClaudeBot + // ClaudeBot (Anthropic) /(claude(?:bot|-web))\/([\w\.]+)/i, // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, + // Facebook / Meta + // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers + /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i, + // Googlebot - http://www.google.com/bot.html - /(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i, + /(google(?:bot|other|-inspectiontool)(?:-image|-video|-news)?|storebot-google)\/?([\w\.]*)/i, + + // Internet Archive (archive.org) + /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, // Sogou Spider /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, @@ -76,14 +86,29 @@ const Crawlers = Object.freeze({ /(y!?j-(?:asr|br[uw]|dscv|mmp|vsidx|wsc))\/([\w\.]+)/i, // Yandex Bots - https://yandex.com/bots - /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i + /(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i, + + // Yeti (Naver) + /(yeti)\/([\w\.]+)/i, + + // YisouSpider + /(yisouspider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], - // Bytespider - // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - [/((?:bytespider|(?=yahoo! )slurp))/i], + [ + // Google Bots + /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, + + // Bytespider + // DataForSeoBot - https://dataforseo.com/dataforseo-bot + // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot + // Qihoo 360Spider + // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + /(360spider-?(?:image|video)?|bytespider|(?:aspiegel|dataforseo|petal|turnitin)bot|(?=yahoo! )slurp)/i + ], [NAME, [TYPE, CRAWLER]] ] }); @@ -174,8 +199,8 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ - // Microsoft Outlook / Thunderbird - [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] + // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird + [/(evolution|kmail2?|kontact|(?:microsoft |mac)outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] ] }); @@ -188,8 +213,15 @@ const Fetchers = Object.freeze({ [ // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit // ChatGPT-User - https://platform.openai.com/docs/plugins/bot + // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / Telegrambot / Twitterbot / UptimeRobot - /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot)\/([\w\.]+)/i, + /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot)\/([\w\.]+)/i, + + // Google Site Verifier + /(google-site-verification)\/([\w\.]+)/i, + + // Meta + /(meta-externalfetcher)\/([\w\.]+)/i, // Slackbot - https://api.slack.com/robots /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, @@ -207,7 +239,7 @@ const Fetchers = Object.freeze({ [NAME, VERSION, [TYPE, FETCHER]], // Google Bots / Snapchat - [/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i], + [/(feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], [NAME, [TYPE, FETCHER]], ] }); @@ -256,8 +288,8 @@ const MediaPlayers = Object.freeze({ /(flrp)\/([\w\.-]+)/i // Flip Player ], [[NAME, 'Flip Player'], VERSION, [TYPE, MEDIAPLAYER]], [ - /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i - // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit + /(fstream|nativehost|queryseekspider)/i + // FStream/NativeHost/QuerySeekSpider ], [NAME, [TYPE, MEDIAPLAYER]], [ /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i @@ -336,14 +368,14 @@ const MediaPlayers = Object.freeze({ ] }); -//////////////////////// -// MODULES / LIBRARIES -/////////////////////// +///////////// +// LIBRARIES +////////////// -const Modules = Object.freeze({ +const Libraries = Object.freeze({ browser : [ // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]] + [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] ] }); @@ -356,7 +388,7 @@ const Bots = Object.freeze({ ...CLIs.browser, ...Crawlers.browser, ...Fetchers.browser, - ...Modules.browser + ...Libraries.browser ] }); @@ -368,6 +400,6 @@ export { Emails, Fetchers, InApps, - MediaPlayers, - Modules + Libraries, + MediaPlayers }; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 0f46125a4..10398d60d 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.2 +// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index cd63a79ff..abd580379 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-rc.2 +/* Helpers for UAParser.js v2.0.0-rc.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index 7c6fd0eff..5e28bf6bb 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-rc.2 +/* Helpers for UAParser.js v2.0.0-rc.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -19,27 +19,29 @@ import { isStandalonePWA } from 'is-standalone-pwa'; const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isAppleSilicon = (res) => { +const isAppleSilicon = (res, useFeatureDetection) => { if (res.os.is(OS.MACOS)) { if (res.cpu.is(CPU.ARM)) { return true; } - try { - const canvas = document.createElement('canvas'); - const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); - const debug = webgl.getExtension('WEBGL_debug_renderer_info'); - const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL); - if (renderer.match(/apple m\d/i)) { - return true; + if (useFeatureDetection) { + try { + const canvas = document.createElement('canvas'); + const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + const debug = webgl.getExtension('WEBGL_debug_renderer_info'); + const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL); + if (renderer.match(/apple m\d/i)) { + return true; + } + } catch { + return false; } - } catch { - return false; } } return false; } -const isBot = (res) => ['cli', 'crawler', 'fetcher', 'module'].includes(res.browser.type); +const isBot = (res) => ['cli', 'crawler', 'fetcher', 'library'].includes(res.browser.type); const isChromeFamily = (res) => res.engine.is(Engine.BLINK); diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index cbf583717..4e64bf44c 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0-rc.2 +// Type definitions for UAParser.js v2.0.0-rc.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman @@ -19,7 +19,7 @@ declare namespace UAParser { } interface ICPU extends IData { - architecture?: 'ia32' | 'ia64' | 'amd64' | 'arm' | 'arm64' | 'armhf' | 'avr' | 'irix' | 'irix64' | 'mips' | 'mips64' | '68k' | 'ppc' | 'sparc' | 'sparc64'; + architecture?: 'ia32' | 'ia64' | 'amd64' | 'arm' | 'arm64' | 'armhf' | 'avr' | 'avr32' | 'irix' | 'irix64' | 'mips' | 'mips64' | '68k' | 'pa-risc' | 'ppc' | 'sparc' | 'sparc64'; } interface IDevice extends IData { @@ -29,7 +29,7 @@ declare namespace UAParser { } interface IEngine extends IData { - name?: 'Amaya' | 'Blink' | 'EdgeHTML' | 'Flow' | 'Gecko' | 'Goanna' | 'iCab' | 'KHTML' | 'LibWeb' | 'Links' | 'Lynx' | 'NetFront' | 'NetSurf' | 'Presto' | 'Tasman' | 'Trident' | 'w3m' | 'WebKit'; + name?: 'Amaya' | 'ArkWeb' | 'Blink' | 'EdgeHTML' | 'Flow' | 'Gecko' | 'Goanna' | 'iCab' | 'KHTML' | 'LibWeb' | 'Links' | 'Lynx' | 'NetFront' | 'NetSurf' | 'Presto' | 'Servo' | 'Tasman' | 'Trident' | 'w3m' | 'WebKit'; version?: string; } diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 3615efd46..c1f07cb6c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-rc.2 +/* UAParser.js v2.0.0-rc.3 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-rc.2', + var LIBVERSION = '2.0.0-rc.3', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 130f21649..80e1770ce 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-rc.2 +/* UAParser.js v2.0.0-rc.3 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-rc.2', + var LIBVERSION = '2.0.0-rc.3', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -74,6 +74,7 @@ GOOGLE = 'Google', HUAWEI = 'Huawei', LENOVO = 'Lenovo', + HONOR = 'Honor', LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', @@ -529,10 +530,14 @@ /\b(sh-?[altvz]?\d\d[a-ekm]?)/i ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ + // Honor + /(?:honor)([-\w ]+)[;\)]/i + ], [MODEL, [VENDOR, HONOR], [TYPE, MOBILE]], [ + // Huawei /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ - /(?:huawei|honor)([-\w ]+)[;\)]/i, + /(?:huawei)([-\w ]+)[;\)]/i, /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ @@ -597,7 +602,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ // Sony - /droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i + /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [ /sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i @@ -663,13 +668,28 @@ /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [ + // Energizer + /; (energy ?\w+)(?: bui|\))/i, + /; energizer ([\w ]+)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Energizer'], [TYPE, MOBILE]], [ + + // Cat + /; cat (b35);/i, + /; (b15q?|s22 flip|s48c|s62 pro)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Cat'], [TYPE, MOBILE]], [ + + // Smartfren + /((?:new )?andromax[\w- ]+)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [ + // Nothing /droid.+; (a(?:015|06[35]|142p?))/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // MIXED - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i, - // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan + /; (imo) ((?!tab)[\w ]+?)(?: bui|\))/i, // IMO /(hp) ([\w ]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i, // Asus /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia @@ -678,6 +698,7 @@ /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + /(imo) (tab \w+)/i, // IMO /(kobo)\s(ereader|touch)/i, // Kobo /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad @@ -811,7 +832,7 @@ ], [VERSION, [NAME, 'Blink']], [ /(presto)\/([\w\.]+)/i, // Presto - /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna + /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna/Servo /ekioh(flow)\/([\w\.]+)/i, // Flow /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab @@ -1238,14 +1259,21 @@ headers = extensions; // case UAParser(ua, headers) extensions = undefined; } + + // Convert Headers object into a plain object + if (headers && typeof headers.append === FUNC_TYPE) { + var kv = {}; + headers.forEach(function (v, k) { kv[k] = v; }); + headers = kv; + } if (!(this instanceof UAParser)) { return new UAParser(ua, extensions, headers).getResult(); } var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string - ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers + ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent EMPTY)), // empty string httpUACH = new UACHData(headers, true), From ba980c8250cdccb57b1181086e6b7a2300fe247d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 14 Nov 2024 11:54:11 +0700 Subject: [PATCH 265/388] Refine changelog details regarding v1 to v2 migration --- CHANGELOG.md | 88 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a16881fe..0e1966dcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,36 +1,68 @@ # UAParser.js Changelog -# Version 2.0 ## Migrating from v1 to v2 -- What's breaking: - - Licensed under AGPLv3 (open-source) or PRO License (commercial) - - Browser detection on mobile device: `"Chrome" => "Mobile Chrome"`, `"Firefox" => "Mobile Firefox"` - - OS detection: `"Mac OS" => "macOS"`, `"Chromium OS" => "Chrome OS"` -- What's new: - - New device type: `xr`, to identify AR/VR devices - - New browser property: `browser.type`, to identify the type of the browser: `crawler`, `cli`, `email`, `fetcher`, `inapp`, `library`, `mediaplayer` - - New methods in result object (all of `get*()` return value): - - Enhance detection using client hints: `withClientHints()` - - Enhance detection using feature check: `withFeatureCheck()` - - Utility for easy comparison: `is()` - - Utility to print full-name: `toString()` - - Parse user-agent directly from command line using `npx ua-parser-js "[User-Agent]"` - - Extensions can be passed as a list to `UAParser()` - - Support for ES module & TypeScript `import { UAParser } from 'ua-parser-js'` - - Provided Enums submodule `'ua-parser-js/enums'` - - Provided Extensions submodule `'ua-parser-js/extensions'` - - Provided Helpers submodule `'ua-parser-js/helpers'`: - - `getDeviceVendor()`: guess for a device vendor based on its model name - - `isAppleSilicon()`: check if the device has Apple Silicon Mac device properties - - `isBot()`: check if the browser is identified as a bot - - `isChromeFamily()`: check if the browser is Chrome-based (has Blink engine, i.e: New Opera, New Edge, Vivaldi, Brave, Arc, etc.) - - `isElectron()`: check if current window is running inside Electron - - `isFromEU()`: check if current window is from an EU (European Union) country - - `isFrozenUA()`: check if a user-agent string match with the reduced/frozen user-agent pattern - - `isStandalonePWA()`: check if current window is a standalone PWA +### What's Breaking: ---- +- **Licensing Changes:** + - UAParser.js is now licensed under AGPLv3 for open-source use, with PRO Licenses available for commercial/proprietary use + +- **Browser Detection on Mobile Devices:** + - `"Chrome"` => `"Mobile Chrome"` + - `"Firefox"` => `"Mobile Firefox"` + +- **OS Detection:** + - `"Mac OS"` => `"macOS"` + - `"Chromium OS"` => `"Chrome OS"` + +### What's New: + +- **Support for ES Modules & TypeScript:** + - Import directly as an ES module with TypeScript support: `import { UAParser } from 'ua-parser-js'` + +- **Support for Custom/Predefined Extensions:** + - Pass custom regexes or predefined extensions as a list to `UAParser()` + +- **Support for CLI Parsing:** + - Parse a user-agent directly from the command line using `npx ua-parser-js "[User-Agent]"` + +- **Enhanced Detection with Client Hints:** + - `withClientHints()`: Improves detection accuracy by leveraging client hints + +- **Enhanced Detection with Feature Detection:** + - `withFeatureCheck()`: Refines detection results using feature detection + +- **Simple Comparison for Detection Results:** + - `is()`: Enables easy comparison checks against the detection result + +- **Detailed Result Output:** + - `toString()`: Returns the detection result in form of a full-name string + +- **New Device Type:** + - Added `xr` to identify AR/VR devices + +- **New Browser Property:** + - Added `browser.type` to identify additional browser types: + - `crawler`, `cli`, `email`, `fetcher`, `inapp`, `library`, `mediaplayer` + +- **New Submodules:** + - **`'ua-parser-js/enums'`**: Provides constants for these specific properties: + - `browser.name`, `browser.type`, `cpu.architecture`, `device.type`, `device.vendor`, `engine.name`, `os.name` + + - **`'ua-parser-js/extensions'`**: Predefined extensions for various use cases: + - `Bots`, `Crawlers`, `CLIs`, `Emails`, `ExtraDevices`, `Fetchers`, `InApps`, `Libraries`, `Mediaplayers` + + - **`'ua-parser-js/helpers'`**: Provides utility methods to extend detection functionality: + - `getDeviceVendor()`: Guesses the device vendor based on its model name + - `isAppleSilicon()`: Detects Apple Silicon device properties + - `isBot()`: Checks if the browser is a bot + - `isChromeFamily()`: Checks if the browser is Chrome-based (uses Blink engine) — e.g., New Opera, New Edge, Vivaldi, Brave, Arc, etc. + - `isElectron()`: Detects if current window is running within Electron + - `isFromEU()`: Detects if current browser's timezone is from an EU country + - `isFrozenUA()`: Checks if the user-agent matches a frozen/reduced user-agent pattern + - `isStandalonePWA()`: Detects if current window is a standalone PWA + +--- ## Version 2.0.0-rc.3 From 4a12562946c935266783db9feed5e1f35589842d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 14 Nov 2024 22:51:41 +0700 Subject: [PATCH 266/388] [submodule:extensions] Add new CLI: ELinks, HTTPie --- src/extensions/ua-parser-extensions.js | 4 ++-- src/main/ua-parser.js | 2 +- test/specs/browser-clis.json | 30 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index a6774ad8b..8734a2fcb 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -28,8 +28,8 @@ const LIBRARY = 'library'; const CLIs = Object.freeze({ browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] + // wget / curl / Lynx / ELinks / HTTPie + [/(wget|curl|lynx|elinks|httpie)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index c1f07cb6c..2c5fe6d43 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -463,7 +463,7 @@ // Other /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i // Links + /\b(links) \(([\w\.]+)/i // Links ], [NAME, [VERSION, /_/g, '.']], [ /(cobalt)\/([\w\.]+)/i // Cobalt diff --git a/test/specs/browser-clis.json b/test/specs/browser-clis.json index 7d344ff15..ce89135d9 100644 --- a/test/specs/browser-clis.json +++ b/test/specs/browser-clis.json @@ -9,6 +9,36 @@ "type" : "cli" } }, + { + "desc" : "ELinks", + "ua" : "ELinks/0.11.4-3-lite (textmode; Debian; Linux 2.6.26-1-686 i686;", + "expect" : + { + "name" : "ELinks", + "version" : "0.11.4-3-lite", + "type" : "cli" + } + }, + { + "desc" : "ELinks", + "ua" : "ELinks (0.11.3; Linux 2.6.23-hardened-r4 i686; 166x55)", + "expect" : + { + "name" : "ELinks", + "version" : "0.11.3", + "type" : "cli" + } + }, + { + "desc" : "HTTPie", + "ua" : "HTTPie/0.9.9", + "expect" : + { + "name" : "HTTPie", + "version" : "0.9.9", + "type" : "cli" + } + }, { "desc" : "lynx", "ua" : "Lynx 2.8.8dev.3", From 14caf6d204df8f57aad848c0f6886c60c6cc8be3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 15 Nov 2024 17:24:45 +0700 Subject: [PATCH 267/388] [submodule:extensions] Add new library: `java`, `python-urllib`, `python-requests` --- src/extensions/ua-parser-extensions.js | 6 ++-- test/specs/browser-libraries.json | 50 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 8734a2fcb..628313cae 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -293,8 +293,6 @@ const MediaPlayers = Object.freeze({ ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player - /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i, - // Java/urllib/requests/wget/cURL /(lavf)([\d\.]+)/i // Lavf (FFMPEG) ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ @@ -370,8 +368,8 @@ const MediaPlayers = Object.freeze({ const Libraries = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] + // Axios/jsdom/Scrapy/Java/urllib/requests + [/\b(axios|jsdom|scrapy|java|python-urllib|python-requests)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] ] }); diff --git a/test/specs/browser-libraries.json b/test/specs/browser-libraries.json index 8e0eb56fb..1ebdcdc48 100644 --- a/test/specs/browser-libraries.json +++ b/test/specs/browser-libraries.json @@ -1,4 +1,54 @@ [ + { + "desc" : "Axios", + "ua" : "axios/1.7.2", + "expect" : + { + "name" : "axios", + "version" : "1.7.2", + "type" : "library" + } + }, + { + "desc" : "Java", + "ua" : "Java/1.6.0_14", + "expect" : + { + "name" : "Java", + "version" : "1.6.0_14", + "type" : "library" + } + }, + { + "desc" : "jsdom", + "ua" : "Mozilla/5.0 (unknown OS) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0", + "expect" : + { + "name" : "jsdom", + "version" : "11.12.0", + "type" : "library" + } + }, + { + "desc" : "Python urllib", + "ua" : "Python-urllib/2.6", + "expect" : + { + "name" : "Python-urllib", + "version" : "2.6", + "type" : "library" + } + }, + { + "desc" : "Python requests", + "ua" : "python-requests/2.32", + "expect" : + { + "name" : "python-requests", + "version" : "2.32", + "type" : "library" + } + }, { "desc" : "Scrapy", "ua" : "Scrapy/1.5.0 (+https://scrapy.org)", From 2181559b01617cbca67f991f8bfb6b983415da4f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 15:28:35 +0700 Subject: [PATCH 268/388] [submodule:extensions] Add new email: Airmail, BlueMail, eMClient, NaverMailApp, Sparrow, Yahoo --- src/extensions/ua-parser-extensions.js | 10 ++- test/specs/browser-emails.json | 90 ++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 628313cae..5e836bbfa 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -42,7 +42,6 @@ const Crawlers = Object.freeze({ [ // AhrefsBot - https://ahrefs.com/robot // Amazonbot - https://developer.amazon.com/amazonbot - // Applebot - http://apple.com/go/applebot // Bingbot - http://www.bing.com/bingbot.htm // CCBot - https://commoncrawl.org/faq // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot @@ -54,7 +53,10 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|apple|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, + /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, + + // Applebot - http://apple.com/go/applebot + /(applebot(?:-extended)?)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, @@ -195,8 +197,10 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ + [ // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird - [/(evolution|kmail2?|kontact|(?:microsoft |mac)outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] + /(airmail|bluemail|emclient|evolution|foxmail|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|thunderbird|yahoo)(?:m.+ail; |[\/ ])([\w\.]+)/i + ], [NAME, VERSION, [TYPE, EMAIL]] ] }); diff --git a/test/specs/browser-emails.json b/test/specs/browser-emails.json index 883f32be6..27fbce58f 100644 --- a/test/specs/browser-emails.json +++ b/test/specs/browser-emails.json @@ -1,4 +1,34 @@ [ + { + "desc" : "Airmail", + "ua" : "Airmail 1.0 rv:148 (Macintosh; Mac OS X 10.8.3; en_BE)", + "expect" : + { + "name" : "Airmail", + "version" : "1.0", + "type" : "email" + } + }, + { + "desc" : "BlueMail", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) BlueMail/0.10.31 Chrome/61.0.3163.100 Electron/2.0.18 Safari/537.36", + "expect" : + { + "name" : "BlueMail", + "version" : "0.10.31", + "type" : "email" + } + }, + { + "desc" : "BlueMail", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A405 BlueMail iOS", + "expect" : + { + "name" : "BlueMail", + "version" : "iOS", + "type" : "email" + } + }, { "desc" : "Evolution", "ua" : "Evolution/3.52.3", @@ -9,6 +39,26 @@ "type" : "email" } }, + { + "desc" : "eM Client", + "ua" : "eMClient/9.2.2157.0", + "expect" : + { + "name" : "eMClient", + "version" : "9.2.2157.0", + "type" : "email" + } + }, + { + "desc" : "Foxmail", + "ua" : "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36 foxmail/7.2.15.80", + "expect" : + { + "name" : "foxmail", + "version" : "7.2.15.80", + "type" : "email" + } + }, { "desc" : "KMail", "ua" : "KMail/4.14.10 (FreeBSD/12.0-CURRENT; KDE/4.14.10; amd64; ; )", @@ -59,6 +109,26 @@ "type" : "email" } }, + { + "desc" : "NaverMailApp", + "ua" : "NaverMailApp/2.1.23 (Android 10; SM-N960N)", + "expect" : + { + "name" : "NaverMailApp", + "version" : "2.1.23", + "type" : "email" + } + }, + { + "desc" : "Sparrow", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Sparrow/1043.1", + "expect" : + { + "name" : "Sparrow", + "version" : "1043.1", + "type" : "email" + } + }, { "desc" : "Thunderbird", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0", @@ -68,5 +138,25 @@ "version" : "78.13.0", "type" : "email" } + }, + { + "desc" : "Yahoo! Mail", + "ua" : "YahooMobile/1.0 (mail; 3.0.5.1311380); (Linux; U; Android 4.0.3; htc_runnymede Build/ICE_CREAM_SANDWICH_MR1);", + "expect" : + { + "name" : "Yahoo", + "version" : "3.0.5.1311380", + "type" : "email" + } + }, + { + "desc" : "Yahoo! Mail", + "ua" : "YahooMobileMail/1.0 (Android Mail; 1.3.10) (supersonic;HTC;PC36100;2.3.5/GRJ90) ", + "expect" : + { + "name" : "Yahoo", + "version" : "1.3.10", + "type" : "email" + } } ] From 2b125c8de45ce77ff735ef78200035cbb8eb733b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 18:40:02 +0700 Subject: [PATCH 269/388] [submodule:extensions] Add new bots: AI2Bot, aiHitBot, anthropic-ai, cohere-ai, Diffbot, ImagesiftBot, magpie-crawler, Omgilibot, Screaming Frog SEO Spider, Seznambot, Teoma, Timpibot, VelenPublicWebCrawler, Vercelbot, Webzio-Extended, YouBot --- src/extensions/ua-parser-extensions.js | 17 ++-- test/specs/browser-crawlers.json | 122 ++++++++++++++++++++++++- test/specs/browser-fetchers.json | 10 ++ 3 files changed, 141 insertions(+), 8 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5e836bbfa..bd9d517d6 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -53,7 +53,8 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, + // SeznamBot - http://napoveda.seznam.cz/seznambot-intro + /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/([\w\.]+)/i, @@ -62,7 +63,7 @@ const Crawlers = Object.freeze({ /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, // ClaudeBot (Anthropic) - /(claude(?:bot|-web))\/([\w\.]+)/i, + /(claude(?:bot|-web)|anthropic-ai)\/?([\w\.]*)/i, // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, @@ -89,8 +90,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // YisouSpider - /(yisouspider)\/?([\w\.]*)/i + // aiHitBot / Cohere-AI / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|cohere-ai|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -99,13 +100,15 @@ const Crawlers = Object.freeze({ // Google Bots /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, + // AI2Bot - https://allenai.org/crawler // Bytespider // DataForSeoBot - https://dataforseo.com/dataforseo-bot // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot + // ImagesiftBot - https://imagesift.com/about // Qihoo 360Spider // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - /(360spider-?(?:image|video)?|bytespider|(?:aspiegel|dataforseo|petal|turnitin)bot|(?=yahoo! )slurp)/i + /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|(?=yahoo! )slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -238,8 +241,8 @@ const Fetchers = Object.freeze({ ], [NAME, VERSION, [TYPE, FETCHER]], - // Google Bots / Snapchat - [/(feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], + // Google Bots / Snapchat / Vercelbot + [/(vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], [NAME, [TYPE, FETCHER]], ] }); diff --git a/test/specs/browser-crawlers.json b/test/specs/browser-crawlers.json index e527740e3..04a1a3525 100644 --- a/test/specs/browser-crawlers.json +++ b/test/specs/browser-crawlers.json @@ -49,6 +49,26 @@ "type" : "crawler" } }, + { + "desc" : "AI2Bot", + "ua" : "Mozilla/5.0 (compatible) AI2Bot (+https://www.allenai.org/crawler)", + "expect" : + { + "name" : "AI2Bot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "aiHitBot", + "ua" : "Mozilla/5.0 (compatible; aiHitBot/2.9; +https://www.aihitdata.com/about)", + "expect" : + { + "name" : "aiHitBot", + "version" : "2.9", + "type" : "crawler" + } + }, { "desc" : "Applebot", "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)", @@ -131,7 +151,7 @@ }, { "desc" : "DataForSEO", - "ua" : "Mozilla/5.0 (compatible; DataForSeoBot; +https://dataforseo.com/dataforseo-bot) ", + "ua" : "Mozilla/5.0 (compatible; DataForSeoBot; +https://dataforseo.com/dataforseo-bot)", "expect" : { "name" : "DataForSeoBot", @@ -139,6 +159,16 @@ "type" : "crawler" } }, + { + "desc" : "Diffbot", + "ua" : "Diffbot/0.1", + "expect" : + { + "name" : "Diffbot", + "version" : "0.1", + "type" : "crawler" + } + }, { "desc" : "Dotbot", "ua" : "Mozilla/5.0 (compatible; DotBot/1.2; +https://opensiteexplorer.org/dotbot; help@moz.com)", @@ -329,6 +359,26 @@ "type" : "crawler" } }, + { + "desc" : "ImagesiftBot", + "ua" : "Mozilla/5.0 (compatible; ImagesiftBot; +imagesift.com)", + "expect" : + { + "name" : "ImagesiftBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "magpie-crawler", + "ua" : "magpie-crawler/1.1 (robots-txt-checker; +http://www.brandwatch.net)", + "expect" : + { + "name" : "magpie-crawler", + "version" : "1.1", + "type" : "crawler" + } + }, { "desc" : "Meta-ExternalAgent", "ua" : "meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", @@ -360,6 +410,26 @@ "type" : "crawler" } }, + { + "desc" : "Omgili", + "ua" : "omgili/0.5 +https://omgili.com", + "expect" : + { + "name" : "omgili", + "version" : "0.5", + "type" : "crawler" + } + }, + { + "desc" : "Omgilibot", + "ua" : "omgilibot/0.3 +http://www.omgili.com/Crawler.html", + "expect" : + { + "name" : "omgilibot", + "version" : "0.3", + "type" : "crawler" + } + }, { "desc" : "OpenAI Search", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot", @@ -410,6 +480,36 @@ "type" : "crawler" } }, + { + "desc" : "SeznamBot", + "ua" : "Mozilla/5.0 (compatible; SeznamBot/4.0-RC1; +http://napoveda.seznam.cz/seznambot-intro/)", + "expect" : + { + "name" : "SeznamBot", + "version" : "4.0-RC1", + "type" : "crawler" + } + }, + { + "desc" : "Teoma", + "ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)", + "expect" : + { + "name" : "Teoma", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Timpibot", + "ua" : "Timpibot/0.8 (+http://www.timpi.io)", + "expect" : + { + "name" : "Timpibot", + "version" : "0.8", + "type" : "crawler" + } + }, { "desc" : "TurnitinBot", "ua" : "TurnitinBot (https://turnitin.com/robot/crawlerinfo.html)", @@ -420,6 +520,16 @@ "type" : "crawler" } }, + { + "desc" : "VelenPublicWebCrawler", + "ua" : "Mozilla/5.0 (compatible; VelenPublicWebCrawler/1.0; +https://velen.io)", + "expect" : + { + "name" : "VelenPublicWebCrawler", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Yahoo! Japan", "ua" : "Y!J-BRW/1.0 (https://www.yahoo-help.jp/app/answers/detail/p/595/a_id/42716)", @@ -469,5 +579,15 @@ "version" : "undefined", "type" : "crawler" } + }, + { + "desc" : "YouBot", + "ua" : "YouBot (+http://www.you.com)", + "expect" : + { + "name" : "YouBot", + "version" : "undefined", + "type" : "crawler" + } } ] diff --git a/test/specs/browser-fetchers.json b/test/specs/browser-fetchers.json index 94bada00e..dfd76f16e 100644 --- a/test/specs/browser-fetchers.json +++ b/test/specs/browser-fetchers.json @@ -118,5 +118,15 @@ "version" : "2.0", "type" : "fetcher" } + }, + { + "desc" : "Vercelbot", + "ua" : "Vercelbot (+https://vercel.com)", + "expect" : + { + "name" : "Vercelbot", + "version" : "undefined", + "type" : "fetcher" + } } ] \ No newline at end of file From b1c7dfcc3a0f24a129fae30f7ccde22d271f0552 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 19:22:43 +0700 Subject: [PATCH 270/388] [submodule:helpers] Revert providing extra param in `isAppleSilicon()` and just check for window instead --- src/helpers/ua-parser-helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index abd580379..4a09605eb 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -15,12 +15,12 @@ const { isStandalonePWA } = require('is-standalone-pwa'); const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isAppleSilicon = (res, useFeatureDetection) => { +const isAppleSilicon = (res) => { if (res.os.is(OS.MACOS)) { if (res.cpu.is(CPU.ARM)) { return true; } - if (useFeatureDetection) { + if (typeof window !== 'undefined') { try { const canvas = document.createElement('canvas'); const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); From 5b375b90d540642a4243a5c1eb24e7f493e21d3c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 21:17:51 +0700 Subject: [PATCH 271/388] [submodule:helpers] Enable directly pass user-agent as an input to `isAppleSilicon()` / `isBot()` / `isChromeFamily()` --- src/helpers/ua-parser-helpers.d.ts | 6 +++--- src/helpers/ua-parser-helpers.js | 17 +++++++++++++---- test/mocha-test-helpers.js | 9 +++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 10398d60d..0e6147761 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -5,9 +5,9 @@ import { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; -declare function isAppleSilicon(res: IResult, useFeatureDetection?: boolean): boolean; -declare function isBot(res: IResult): boolean; -declare function isChromeFamily(res: IResult): boolean; +declare function isAppleSilicon(resultOrUA: IResult | string): boolean; +declare function isBot(resultOrUA: IResult | string): boolean; +declare function isChromeFamily(resultOrUA: IResult | string): boolean; declare function isElectron(): boolean; declare function isFromEU(): boolean; declare function isFrozenUA(ua: string): boolean; diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 4a09605eb..f3f19c80f 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -9,18 +9,22 @@ const { UAParser } = require('../main/ua-parser'); const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); +const { Bots } = require('../extensions/ua-parser-extensions'); const { isFromEU } = require('detect-europe-js'); const { isFrozenUA } = require('ua-is-frozen'); const { isStandalonePWA } = require('is-standalone-pwa'); +const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value; + const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isAppleSilicon = (res) => { +const isAppleSilicon = (resultOrUA) => { + const res = toResult(resultOrUA); if (res.os.is(OS.MACOS)) { if (res.cpu.is(CPU.ARM)) { return true; } - if (typeof window !== 'undefined') { + if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') { try { const canvas = document.createElement('canvas'); const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); @@ -37,9 +41,14 @@ const isAppleSilicon = (res) => { return false; } -const isBot = (res) => ['cli', 'crawler', 'fetcher', 'library'].includes(res.browser.type); +const isBot = (resultOrUA) => [ + 'cli', + 'crawler', + 'fetcher', + 'library' + ].includes(toResult(resultOrUA, Bots).browser.type); -const isChromeFamily = (res) => res.engine.is(Engine.BLINK); +const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(Engine.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js / electron\//i.test(navigator?.userAgent)); // browser diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index ab61cd939..a2f25cdc2 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -28,7 +28,9 @@ describe('isAppleSilicon', () => { const macIntel = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0'; assert.equal(isAppleSilicon(UAParser(macIntel)), false); + assert.equal(isAppleSilicon(macIntel), false); assert.equal(isAppleSilicon(UAParser(macARM)), true); + assert.equal(isAppleSilicon(macARM), true); }); }); @@ -46,6 +48,11 @@ describe('isBot', () => { assert.equal(isBot(botParser.setUA(ahrefsBot).getResult()), true); assert.equal(isBot(botParser.setUA(scrapy).getResult()), true); assert.equal(isBot(botParser.setUA(thunderbird).getResult()), false); + + assert.equal(isBot(ahrefsBot), true); + assert.equal(isBot(firefox), false); + assert.equal(isBot(scrapy), true); + assert.equal(isBot(thunderbird), false); }); }); @@ -57,5 +64,7 @@ describe('isChromeFamily', () => { assert.equal(isChromeFamily(UAParser(edge)), true); assert.equal(isChromeFamily(UAParser(firefox)), false); + assert.equal(isChromeFamily(edge), true); + assert.equal(isChromeFamily(firefox), false); }); }); \ No newline at end of file From 70b3003344dd7dc4d815406d955ac48857d89c55 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 16 Nov 2024 22:14:14 +0700 Subject: [PATCH 272/388] [submodule:helpers] Add new method `isAIBot()`: detect AI bots --- README.md | 10 +++- src/extensions/ua-parser-extensions.js | 8 +-- src/helpers/ua-parser-helpers.d.ts | 2 + src/helpers/ua-parser-helpers.js | 72 ++++++++++++++++++++++++++ test/mocha-test-helpers.js | 16 +++++- 5 files changed, 102 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7b0aa5fa8..aa7e1c0b7 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,15 @@ see what's new & breaking. ✅ - Extras (Apps, Libs, Emails, Media Players, etc) + AI Bot detection + ❌ + ✅ + ✅ + ✅ + ✅ + + + Extras (Apps, Libs, Emails, Media Players, etc) detection ❌ ✅ ✅ diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index bd9d517d6..5ed4f758b 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -90,8 +90,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Cohere-AI / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|cohere-ai|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -241,8 +241,8 @@ const Fetchers = Object.freeze({ ], [NAME, VERSION, [TYPE, FETCHER]], - // Google Bots / Snapchat / Vercelbot - [/(vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], + // Google Bots / Cohere / Snapchat / Vercelbot + [/(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], [NAME, [TYPE, FETCHER]], ] }); diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 0e6147761..4564a23c2 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -6,6 +6,7 @@ import { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; declare function isAppleSilicon(resultOrUA: IResult | string): boolean; +declare function isAIBot(resultOrUA: IResult | string): boolean; declare function isBot(resultOrUA: IResult | string): boolean; declare function isChromeFamily(resultOrUA: IResult | string): boolean; declare function isElectron(): boolean; @@ -16,6 +17,7 @@ declare function isStandalonePWA(): boolean; export { getDeviceVendor, isAppleSilicon, + isAIBot, isBot, isChromeFamily, isElectron, diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index f3f19c80f..17946ee93 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -41,6 +41,77 @@ const isAppleSilicon = (resultOrUA) => { return false; } +const isAIBot = (resultOrUA) => [ + + // AI2 + 'ai2bot', + + // Amazon + 'amazonbot', + + // Anthropic + 'anthropic-ai', + 'claude-web', + 'claudebot', + + // Apple + 'applebot', + 'applebot-extended', + + // ByteDance + 'bytespider', + + // Common Crawl + 'ccbot', + + // DataForSeo + 'dataforseobot', + + // Diffbot + 'diffbot', + + // Google + 'googleother', + 'googleother-image', + 'googleother-video', + 'google-extended', + + // Hive AI + 'imagesiftbot', + + // Huawei + 'petalbot', + + // Meta + 'facebookbot', + 'meta-externalagent', + + // OpenAI + 'gptbot', + 'oai-searchbot', + + // Perplexity + 'perplexitybot', + + // Timpi + 'timpibot', + + // Velen.io + 'velenpublicwebcrawler', + + // Webz.io + 'omgili', + 'omgilibot', + 'webzio-extended', + + // You.com + 'youbot', + + // Zyte + 'scrapy' + + ].includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + const isBot = (resultOrUA) => [ 'cli', 'crawler', @@ -56,6 +127,7 @@ const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || module.exports = { getDeviceVendor, isAppleSilicon, + isAIBot, isBot, isChromeFamily, isElectron, diff --git a/test/mocha-test-helpers.js b/test/mocha-test-helpers.js index a2f25cdc2..d6170bcb5 100644 --- a/test/mocha-test-helpers.js +++ b/test/mocha-test-helpers.js @@ -1,6 +1,6 @@ const assert = require('assert'); const { UAParser } = require('../src/main/ua-parser'); -const { getDeviceVendor, isAppleSilicon, isBot, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); +const { getDeviceVendor, isAppleSilicon, isAIBot, isBot, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); const { Bots, Emails } = require('../src/extensions/ua-parser-extensions'); describe('getDeviceVendor', () => { @@ -34,6 +34,20 @@ describe('isAppleSilicon', () => { }); }); +describe('isAIBot', () => { + it('Can detect AI Bots', () => { + + const claudeBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)'; + const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; + const searchGPT = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot'; + + assert.equal(isAIBot(UAParser(claudeBot, Bots)), true); + assert.equal(isAIBot(claudeBot), true); + assert.equal(isAIBot(firefox), false); + assert.equal(isAIBot(searchGPT), true); + }); +}); + describe('isBot', () => { it('Can detect Bots', () => { From 38baf844fc3e96f72d9b91e0772c8c10655fd5f7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Nov 2024 07:54:54 +0700 Subject: [PATCH 273/388] Update version to `2.0.0` --- CHANGELOG.md | 14 +++- dist/ua-parser.min.js | 4 +- dist/ua-parser.pack.js | 4 +- package-lock.json | 4 +- package.json | 5 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 2 +- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 37 +++++----- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 91 ++++++++++++++++++++++-- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 6 +- 16 files changed, 141 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1966dcf..0be492a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,8 @@ - **`'ua-parser-js/helpers'`**: Provides utility methods to extend detection functionality: - `getDeviceVendor()`: Guesses the device vendor based on its model name - `isAppleSilicon()`: Detects Apple Silicon device properties - - `isBot()`: Checks if the browser is a bot + - `isAIBot()`: Checks if the user-agent is an AI bot + - `isBot()`: Checks if the user-agent is a bot - `isChromeFamily()`: Checks if the browser is Chrome-based (uses Blink engine) — e.g., New Opera, New Edge, Vivaldi, Brave, Arc, etc. - `isElectron()`: Detects if current window is running within Electron - `isFromEU()`: Detects if current browser's timezone is from an EU country @@ -64,6 +65,17 @@ --- +## Version 2.0.0 + +- `ua-parser-js/extensions` submodule: + - Add new CLI: ELinks, HTTPie + - Add new crawler: AI2Bot, aiHitBot, anthropic-ai, Diffbot, ImagesiftBot, magpie-crawler, Omgilibot, Screaming Frog SEO Spider, Seznambot, Teoma, Timpibot, VelenPublicWebCrawler, Webzio-Extended, YouBot + - Add new email: Airmail, BlueMail, eMClient, NaverMailApp, Sparrow, Yahoo + - Add new fetcher: cohere-ai, Vercelbot + - Add new library: java, python-urllib, python-requests +- `ua-parser-js/helpers` submodule: + - Add new method `isAIBot()`: Checks if the user-agent is an AI bot + ## Version 2.0.0-rc.3 - Add support for Headers object diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index bfc76aa6f..54ff4b67d 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-rc.3 +/* UAParser.js v2.0.0 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0-rc.3",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.0",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 55b1df175..6f7b1fb00 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0-rc.3 +/* UAParser.js v2.0.0 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -!function(i,d){"use strict";function e(i){for(var e={},t=0;tC?Ci(i,C):i),this}]]).setUA(r),this}Ui.VERSION="2.0.0-rc.3",Ui.BROWSER=e([m,v,p,f]),Ui.CPU=e([k]),Ui.DEVICE=e([h,g,f,x,y,t,r,o,s]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):li&&(i.UAParser=Ui);var ji,Ei=li&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(ji=new Ui,Ei.ua=ji.getResult(),Ei.ua.get=function(){return ji.getUA()},Ei.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +!function(i,d){"use strict";function e(i){for(var e={},t=0;tC?Ci(i,C):i),this}]]).setUA(r),this}Ui.VERSION="2.0.0",Ui.BROWSER=e([m,v,p,f]),Ui.CPU=e([k]),Ui.DEVICE=e([h,g,f,x,y,t,r,o,s]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):li&&(i.UAParser=Ui);var ji,Ei=li&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(ji=new Ui,Ei.ua=ji.getResult(),Ei.ua.get=function(){return ji.getUA()},Ei.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 42acf6be8..56374f456 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0-rc.3", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0-rc.3", + "version": "2.0.0", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index 73f74c852..5a5397823 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0-rc.3", + "version": "2.0.0", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ @@ -180,7 +180,8 @@ }, "./enums": { "require": "./src/enums/ua-parser-enums.js", - "import": "./src/enums/ua-parser-enums.mjs" + "import": "./src/enums/ua-parser-enums.mjs", + "types": "./src/enums/ua-parser-enums.d.ts" }, "./extensions": { "require": "./src/extensions/ua-parser-extensions.js", diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ff0156d90..ab4629668 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-rc.3 +/* Enums for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index dff46cb72..c9a2f0fb8 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0-rc.3 +/* Enums for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index b4da5499b..d1d0a12a3 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.0 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5ed4f758b..2766271bf 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-rc.3 +/* Extensions for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 5fc374d5d..20fbee6d2 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0-rc.3 +/* Extensions for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -32,8 +32,8 @@ const LIBRARY = 'library'; const CLIs = Object.freeze({ browser : [ - // wget / curl / lynx - [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]] + // wget / curl / Lynx / ELinks / HTTPie + [/(wget|curl|lynx|elinks|httpie)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]] ] }); @@ -46,7 +46,6 @@ const Crawlers = Object.freeze({ [ // AhrefsBot - https://ahrefs.com/robot // Amazonbot - https://developer.amazon.com/amazonbot - // Applebot - http://apple.com/go/applebot // Bingbot - http://www.bing.com/bingbot.htm // CCBot - https://commoncrawl.org/faq // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot @@ -58,13 +57,17 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SemrushBot - http://www.semrush.com/bot.html - /((?:ahrefs|amazon|apple|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush)bot)\/([\w\.]+)/i, + // SeznamBot - http://napoveda.seznam.cz/seznambot-intro + /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + + // Applebot - http://apple.com/go/applebot + /(applebot(?:-extended)?)\/([\w\.]+)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, // ClaudeBot (Anthropic) - /(claude(?:bot|-web))\/([\w\.]+)/i, + /(claude(?:bot|-web)|anthropic-ai)\/?([\w\.]*)/i, // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, @@ -91,8 +94,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // YisouSpider - /(yisouspider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -101,13 +104,15 @@ const Crawlers = Object.freeze({ // Google Bots /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, + // AI2Bot - https://allenai.org/crawler // Bytespider // DataForSeoBot - https://dataforseo.com/dataforseo-bot // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot + // ImagesiftBot - https://imagesift.com/about // Qihoo 360Spider // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - /(360spider-?(?:image|video)?|bytespider|(?:aspiegel|dataforseo|petal|turnitin)bot|(?=yahoo! )slurp)/i + /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|(?=yahoo! )slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -199,8 +204,10 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ + [ // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird - [/(evolution|kmail2?|kontact|(?:microsoft |mac)outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]] + /(airmail|bluemail|emclient|evolution|foxmail|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|thunderbird|yahoo)(?:m.+ail; |[\/ ])([\w\.]+)/i + ], [NAME, VERSION, [TYPE, EMAIL]] ] }); @@ -238,8 +245,8 @@ const Fetchers = Object.freeze({ ], [NAME, VERSION, [TYPE, FETCHER]], - // Google Bots / Snapchat - [/(feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], + // Google Bots / Cohere / Snapchat / Vercelbot + [/(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], [NAME, [TYPE, FETCHER]], ] }); @@ -297,8 +304,6 @@ const MediaPlayers = Object.freeze({ ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player - /(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i, - // Java/urllib/requests/wget/cURL /(lavf)([\d\.]+)/i // Lavf (FFMPEG) ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ @@ -374,8 +379,8 @@ const MediaPlayers = Object.freeze({ const Libraries = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy - [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] + // Axios/jsdom/Scrapy/Java/urllib/requests + [/\b(axios|jsdom|scrapy|java|python-urllib|python-requests)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] ] }); diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 4564a23c2..30a2c810b 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0-rc.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.0 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 17946ee93..4af5175d3 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-rc.3 +/* Helpers for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index 5e28bf6bb..c8d166955 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0-rc.3 +/* Helpers for UAParser.js v2.0.0 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -13,18 +13,22 @@ import { UAParser } from '../main/ua-parser.mjs'; import { CPU, OS, Engine } from '../enums/ua-parser-enums.mjs'; +import { Bots } from '../extensions/ua-parser-extensions.mjs'; import { isFromEU } from 'detect-europe-js'; import { isFrozenUA } from 'ua-is-frozen'; import { isStandalonePWA } from 'is-standalone-pwa'; +const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value; + const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; -const isAppleSilicon = (res, useFeatureDetection) => { +const isAppleSilicon = (resultOrUA) => { + const res = toResult(resultOrUA); if (res.os.is(OS.MACOS)) { if (res.cpu.is(CPU.ARM)) { return true; } - if (useFeatureDetection) { + if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') { try { const canvas = document.createElement('canvas'); const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); @@ -41,9 +45,85 @@ const isAppleSilicon = (res, useFeatureDetection) => { return false; } -const isBot = (res) => ['cli', 'crawler', 'fetcher', 'library'].includes(res.browser.type); +const isAIBot = (resultOrUA) => [ + + // AI2 + 'ai2bot', + + // Amazon + 'amazonbot', + + // Anthropic + 'anthropic-ai', + 'claude-web', + 'claudebot', + + // Apple + 'applebot', + 'applebot-extended', + + // ByteDance + 'bytespider', + + // Common Crawl + 'ccbot', + + // DataForSeo + 'dataforseobot', + + // Diffbot + 'diffbot', + + // Google + 'googleother', + 'googleother-image', + 'googleother-video', + 'google-extended', + + // Hive AI + 'imagesiftbot', + + // Huawei + 'petalbot', + + // Meta + 'facebookbot', + 'meta-externalagent', + + // OpenAI + 'gptbot', + 'oai-searchbot', + + // Perplexity + 'perplexitybot', + + // Timpi + 'timpibot', + + // Velen.io + 'velenpublicwebcrawler', + + // Webz.io + 'omgili', + 'omgilibot', + 'webzio-extended', + + // You.com + 'youbot', + + // Zyte + 'scrapy' + + ].includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + +const isBot = (resultOrUA) => [ + 'cli', + 'crawler', + 'fetcher', + 'library' + ].includes(toResult(resultOrUA, Bots).browser.type); -const isChromeFamily = (res) => res.engine.is(Engine.BLINK); +const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(Engine.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js / electron\//i.test(navigator?.userAgent)); // browser @@ -51,6 +131,7 @@ const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || export { getDeviceVendor, isAppleSilicon, + isAIBot, isBot, isChromeFamily, isElectron, diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 4e64bf44c..150d8672f 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0-rc.3 +// Type definitions for UAParser.js v2.0.0 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 2c5fe6d43..b6feb1a05 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-rc.3 +/* UAParser.js v2.0.0 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-rc.3', + var LIBVERSION = '2.0.0', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 80e1770ce..09dbce366 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0-rc.3 +/* UAParser.js v2.0.0 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0-rc.3', + var LIBVERSION = '2.0.0', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', @@ -465,7 +465,7 @@ // Other /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i // Links + /\b(links) \(([\w\.]+)/i // Links ], [NAME, [VERSION, /_/g, '.']], [ /(cobalt)\/([\w\.]+)/i // Cobalt From d1f691fbc775d4ee179788c674280e0286deea72 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Nov 2024 16:06:23 +0700 Subject: [PATCH 274/388] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa7e1c0b7..41f504ff4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@

- + + +

+[![https://uaparser.dev](https://github.com/user-attachments/assets/9f30f3d4-5cfe-441c-8f86-ead7c955f940)](https://uaparser.dev) +

@@ -23,6 +27,7 @@ user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser * Live demo: https://uaparser.dev + # Documentation * `version 1.x` : https://github.com/faisalman/ua-parser-js/tree/1.0.x#documentation From 8174330d27eeb84086e9d42e067ba98baad91fd0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 18 Nov 2024 23:38:41 +0700 Subject: [PATCH 275/388] Update README.md (2) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 41f504ff4..f1f9b15c3 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@

[![https://uaparser.dev](https://github.com/user-attachments/assets/9f30f3d4-5cfe-441c-8f86-ead7c955f940)](https://uaparser.dev) + +[![https://uaparser.dev](https://github.com/user-attachments/assets/50da50fc-7c8a-46e3-a2bc-6a8249914372)](https://uaparser.dev)

From 5742ba68aea239413d78f60bc4328b1255b23e7e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 19 Nov 2024 10:18:08 +0700 Subject: [PATCH 276/388] Update README.md (3) --- README.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f1f9b15c3..cd7772852 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,7 @@ -

- - - -

- +[![https://uaparser.dev](https://raw.githubusercontent.com/faisalman/ua-parser-js/gh-pages/images/uap-header.png)](https://uaparser.dev) [![https://uaparser.dev](https://github.com/user-attachments/assets/9f30f3d4-5cfe-441c-8f86-ead7c955f940)](https://uaparser.dev) - [![https://uaparser.dev](https://github.com/user-attachments/assets/50da50fc-7c8a-46e3-a2bc-6a8249914372)](https://uaparser.dev) +[![https://uaparser.dev](https://github.com/user-attachments/assets/9f2aaff0-a9b4-4ac9-bdf3-eea8081a2582)](https://uaparser.dev)

From 41de949809b941fd35272f280541e8c04981fe3f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 19 Nov 2024 23:41:43 +0700 Subject: [PATCH 277/388] Improve device detection for tablets: Honor, Huawei, Infinix --- src/main/ua-parser.js | 13 +- test/specs/device-all.json | 621 +++++++++++++++++++++++++++++++++++++ 2 files changed, 630 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b6feb1a05..21021a558 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -529,11 +529,13 @@ ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ // Honor - /(?:honor)([-\w ]+)[;\)]/i + /\b((?:brt|eln|hey2?|gdi|jdn)-a?[lnw]09|(?:ag[rm]3?|jdn2|kob2)-a?[lw]0[09]hn)(?: bui|\)|;)/i + ], [MODEL, [VENDOR, HONOR], [TYPE, TABLET]], [ + /honor([-\w ]+)[;\)]/i ], [MODEL, [VENDOR, HONOR], [TYPE, MOBILE]], [ // Huawei - /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i + /\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ /(?:huawei)([-\w ]+)[;\)]/i, /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i @@ -685,9 +687,13 @@ ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // MIXED + /(imo) (tab \w+)/i, // IMO + /(infinix) (x1101b?)/i // Infinix XPad + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (imo) ((?!tab)[\w ]+?)(?: bui|\))/i, // IMO + /; (imo) ([\w ]+?)(?: bui|\))/i, // IMO /(hp) ([\w ]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i, // Asus /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia @@ -696,7 +702,6 @@ /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ - /(imo) (tab \w+)/i, // IMO /(kobo)\s(ereader|touch)/i, // Kobo /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad diff --git a/test/specs/device-all.json b/test/specs/device-all.json index f0898b9f1..ce131ef8f 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -350,6 +350,141 @@ "type": "mobile" } }, + { + "desc": "Honor MagicPad 13 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; GDI-W09 Build/HONORGDI-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "GDI-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 2", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; en-nz; JDN-W09 Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/6.0 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "JDN-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 2", + "ua": "Mozilla/5.0 (Linux; U; Android 9; zh-Hans-CN; JDN2-W09HN Build/HUAWEIJDN2-W09HN) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/4.6.6.164 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "JDN2-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 7 10.1", + "ua": "Mozilla/5.0 (Linux; Android 12; AGM3-AL09HN Build/HONORAGM3-AL09HN; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.46 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/490.0.0.63.82;IABMV/1;]", + "expect": { + "vendor": "Honor", + "model": "AGM3-AL09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 8 12.0", + "ua": "Mozilla/5.0 (Linux; Android 12; HEY-W09 Build/HONORHEY-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "HEY-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 9 12.1", + "ua": "Mozilla/5.0 (Linux; Android 13; HEY2-N09 Build/HONORHEY2-N09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 [FB_IAB/FB4A;FBAV/465.0.0.63.83;]", + "expect": { + "vendor": "Honor", + "model": "HEY2-N09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 9 12.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; HEY2-W09 Build/HONORHEY2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.102 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", + "expect": { + "vendor": "Honor", + "model": "HEY2-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad V7 Pro 11", + "ua": "Mozilla/5.0 (Linux; Android 12; BRT-AN09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 EdgA/109.0.1518.53", + "expect": { + "vendor": "Honor", + "model": "BRT-AN09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad V7 Pro 11 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; BRT-W09 Build/HONORBRT-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.5.0.336 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "BRT-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X6", + "ua": "Mozilla/5.0 (Linux; Android 10; AGR-W09HN Build/HUAWEIAGR-W09HN; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.9 SP-engine/2.28.0 baiduboxapp/12.9.0.11 (Baidu; P1 10) NABar/1.0", + "expect": { + "vendor": "Honor", + "model": "AGR-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X7 8 LTE", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-AL00HN; HMSCore 6.0.0.306) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.3.300 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "KOB2-AL00HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X7 8 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-W09HN; HMSCore 6.1.0.314) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.0.0.301 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "KOB2-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X8 Lite", + "ua": "Mozilla/5.0 (Linux; Android 12; AGM-W09HN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "AGM-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X9 11.5 LTE", + "ua": "Mozilla/5.0 (Linux; Android 13; ELN-L09 Build/HONORELN-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Mobile Safari/537.36[FBAN/EMA;FBLC/zh_CN;FBAV/432.0.0.9.110;FBCX/modulariab;]", + "expect": { + "vendor": "Honor", + "model": "ELN-L09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X9 11.5 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 13; ELN-W09 Build/HONORELN-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "ELN-W09", + "type": "tablet" + } + }, { "desc": "HTC Desire 820", "ua": "Mozilla/5.0 (Linux; Android 6.0.1; HTC Desire 820 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36", @@ -431,6 +566,60 @@ "type": "mobile" } }, + { + "desc": "HUAWEI MediaPad C5 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; MON-AL19B Build/HUAWEIMON-AL19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.83 Mobile Safari/537.36 T7/11.7 baiduboxapp/11.7.0.10 (Baidu; P1 7.0)", + "expect": { + "vendor": "Huawei", + "model": "MON-AL19B", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M2 10.1", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; HUAWEI M2-A01L Build/HUAWEIM2-A01L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/95.0.4638.74 Safari/537.36[FBAN/EMA;FBLC/fr_FR;FBAV/421.0.0.14.100;]", + "expect": { + "vendor": "Huawei", + "model": "M2-A01L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; en-US; BTV-DL09 Build/HUAWEIBEETHOVEN-DL09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 UCBrowser/11.5.0.1015 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTV-DL09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; HUAWEI BTV-W09 Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.96 Mobile Safari/537.36 AlohaBrowser/3.1.1", + "expect": { + "vendor": "Huawei", + "model": "BTV-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 Lite", + "ua": "Mozilla/5.0 (Linux; Android 7.0; CPN-L09 Build/HUAWEICPN-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36[FBAN/EMA;FBLC/ru_RU;FBAV/233.0.0.12.118;]", + "expect": { + "vendor": "Huawei", + "model": "CPN-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 Lite", + "ua": "Mozilla/5.0 (Linux; Android 7.0; CPN-W09 Build/HUAWEICPN-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.71 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/374.0.0.20.109;]", + "expect": { + "vendor": "Huawei", + "model": "CPN-W09", + "type": "tablet" + } + }, { "desc": "HUAWEI MediaPad M3 Lite 10", "ua": "Mozilla/5.0 (Linux; Android 7.0; BAH-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Safari/537.36", @@ -440,6 +629,15 @@ "type": "tablet" } }, + { + "desc": "HUAWEI MediaPad M5 10.8", + "ua": "Mozilla/5.0 (Linux; Android 9; CMR-W09 Build/HUAWEICMR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.102 Safari/537.36 Line/14.18.1/IAB", + "expect": { + "vendor": "Huawei", + "model": "CMR-W09", + "type": "tablet" + } + }, { "desc": "HUAWEI MediaPad M5 Lite", "ua": "Mozilla/5.0 (Linux; Android 8.0.0; BAH2-W19 Build/HUAWEIBAH2-W19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Safari/537.36", @@ -449,6 +647,33 @@ "type": "tablet" } }, + { + "desc": "HUAWEI MediaPad M5 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; JDN2-W09 Build/HUAWEIJDN2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.210 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/318.0.0.39.154;]", + "expect": { + "vendor": "Huawei", + "model": "JDN2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; JDN2-AL50 Build/HUAWEIJDN2-AL50; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.13.0 SP-engine/2.29.0 matrixstyle/0 lite baiduboxapp/5.8.0.10 (Baidu; P1 9) NABar/1.", + "expect": { + "vendor": "Huawei", + "model": "JDN2-AL50", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 8.4", + "ua": "Mozilla/5.0 (Linux; Android 9; SHT-W09 Build/HUAWEISHT-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.87 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SHT-W09", + "type": "tablet" + } + }, { "desc": "HUAWEI MediaPad M5", "ua": "Mozilla/5.0 (Linux; Android 9; SHT-AL09 Build/HUAWEISHT-AL09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.90 Mobile Safari/537.36", @@ -458,6 +683,24 @@ "type": "tablet" } }, + { + "desc": "HUAWEI MediaPad M6 10.8", + "ua": "Mozilla/5.0 (Linux; Android 14; SCM-W09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6612.143 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SCM-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M6 8.4", + "ua": "Mozilla/5.0 (Linux; Android 9; VRD-W09; HMSCore 6.14.0.321; GMSCore 22.26.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "VRD-W09", + "type": "tablet" + } + }, { "desc": "HUAWEI MediaPad T5", "ua": "Mozilla/5.0 (Linux; Android 8.0.0; AGS2-L09 Build/HUAWEIAGS2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.125 Safari/537.36", @@ -467,6 +710,15 @@ "type": "tablet" } }, + { + "desc": "HUAWEI MediaPad T10", + "ua": "Mozilla/5.0 (Linux; U; Android 10; en-US; AGR-L09 Build/HUAWEIAGR-L09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.3.8.1305 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGR-L09", + "type": "tablet" + } + }, { "desc": "HUAWEI MediaPad T10", "ua": "Mozilla/5.0 (Linux; Android 10; AGR-W09 Build/HUAWEIAGR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36", @@ -485,6 +737,339 @@ "type": "tablet" } }, + { + "desc": "HUAWEI MediaPad T 8.0", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-L09 Build/HUAWEIKOB2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/396.0.0.21.104;]", + "expect": { + "vendor": "Huawei", + "model": "KOB2-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T 8.0", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-W09 Build/HUAWEIKOB2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 Mobile Safari/537.36 HuaweiBrowser/15.0.4.312 HMSCore/6.14.0.301", + "expect": { + "vendor": "Huawei", + "model": "KOB2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; T1-A21w Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Safari/537.36 SputnikBrowser/1.2.8.161", + "expect": { + "vendor": "Huawei", + "model": "T1-A21w", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; T1-A23L Build/HuaweiMediaPad; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.121 Mobile Safari/537.36 BingWeb/6.9.10", + "expect": { + "vendor": "Huawei", + "model": "T1-A23L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; T1-A21L Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "T1-A21L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 7", + "ua": "Mozilla/5.0 (Linux; 4.4.2; T1-701u) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "T1-701u", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 8", + "ua": "Mozilla/5.0 (Linux; U; Android 9.0; MediaPad T1 8.0 Build/HuaweiMediaPad) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 OPR/28.0.2254.119224", + "expect": { + "vendor": "Huawei", + "model": "MediaPad T1 8.0", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10 9.7", + "ua": "Mozilla/5.0 (Linux; U; Android 10; en-US; AGRK-L09 Build/HUAWEIAGRK-L09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.6.0.1315 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGRK-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10 9.7 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; AGRK-W09; HMSCore 6.14.0.321) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGRK-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10s 10.1 LTE", + "ua": "Mozilla/5.0 (Linux; Android 10; AGS3K-L09 Build/HUAWEIAGS3K-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36 [FB_IAB/FB4A;FBAV/362.0.0.27.109;]", + "expect": { + "vendor": "Huawei", + "model": "AGS3K-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10s 10.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; AGS3K-W09; HMSCore 6.14.0.321) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS3K-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T2 10.0 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; 605HW Build/HuaweiMediaPad; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.5005.78 Safari/537.36 [FB_IAB/FB4A;FBAV/436.0.0.35.101;]", + "expect": { + "vendor": "Huawei", + "model": "605HW", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T2 7.0 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0; BGO-DL09 Build/HuaweiBAGGIO; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/407.0.0.30.97;]", + "expect": { + "vendor": "Huawei", + "model": "BGO-DL09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; AGS-W09 Build/HUAWEIAGS-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.90 Safari/537.36 GSA/10.83.10.21.arm64", + "expect": { + "vendor": "Huawei", + "model": "AGS-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 7", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BG2-U03 Build/HUAWEIBG2-U03; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.111 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BG2-U03", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; KOB-W09 Build/HUAWEIKOB-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Safari/537.36 [FB_IAB/Orca-Android;FBAV/354.0.0.10.113;]", + "expect": { + "vendor": "Huawei", + "model": "KOB-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T5 10", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; AGS2-W09 Build/HUAWEIAGS2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Huawei", + "model": "AGS2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad X2", + "ua": "Mozilla/5.0 (Linux; Android 8.0; GEM-703L Build/HUAWEIGEM-703L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/043906 Mobile Safari/537.36 MicroMessenger/6.6.3.1260(0x26060339) NetType/WIFI Language/zh_", + "expect": { + "vendor": "Huawei", + "model": "GEM-703L", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH3-W09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH3-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH3-L09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH3-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; BAH3-W59 Build/HUAWEIBAH3-W59; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36HiSearch/22.0.6.315", + "expect": { + "vendor": "Huawei", + "model": "BAH3-W59", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 (2022)", + "ua": "Mozilla/5.0 (Linux; Android 10; BAH4-L09 Build/HUAWEIBAH4-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH4-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 (2022) WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH4-W09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH4-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 SE", + "ua": "Mozilla/5.0 (Linux; Android 12; AGS5-L09 Build/HUAWEIAGS5-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS5-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 SE WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; AGS5-W09 Build/HUAWEIAGS5-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Safari/537.36 [FB_IAB/FB4A;FBAV/480.0.0.54.88;]", + "expect": { + "vendor": "Huawei", + "model": "AGS5-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11 (2023) WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; DBR-W10 Build/HUAWEIDBR-W10) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.9.6.501 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DBR-W10", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-cn; DBY-W09 Build/HUAWEIDBY-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.86 MQQBrowser/14.6 Mobile Safari/537.36 COVC/046801", + "expect": { + "vendor": "Huawei", + "model": "DBY-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 Air WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; DBY2-W00 Build/HUAWEIDBY2-W00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/7.3.8.663 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DBY2-W00", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 LTE", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; BTK-AL09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTK-AL09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 S WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; TGR-W09; HMSCore 6.14.0.322; GMSCore 0.3.3.1.240913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 HuaweiBrowser/14.0.2.317 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "TGR-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; BTK-W09; HMSCore 6.14.0.322; GMSCore 214816056) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTK-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad C5 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; MON-W19 Build/HUAWEIMON-W19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36 [Pinterest/Android]", + "expect": { + "vendor": "Huawei", + "model": "MON-W19", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; GOT-AL09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 YaBrowser/23.5.5.60.01 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "GOT-AL09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 11 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; GOT-W09 Build/HUAWEIGOT-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Safari/537.36 T7/13.19 BDOS/1.0 (HarmonyOS 3.0.0) SP-engine/2.57.0 baiduboxapp/13.19.0.12 (Baidu; P1 12) NABar/1.0", + "expect": { + "vendor": "Huawei", + "model": "GOT-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 12.6 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; WGR-W09 Build/HUAWEIWGR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/412.0.0.8.106;]", + "expect": { + "vendor": "Huawei", + "model": "WGR-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad SE 11 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; AGS6-W09; HMSCore 6.12.2.309) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.5.315 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS6-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 13.2", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; PCE-W29; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "PCE-W29", + "type": "tablet" + } + }, { "desc": "Huawei MatePad T 10", "ua": "Mozilla/5.0 (Linux; Android 10; AGR-L09; HMSCore 5.0.4.301) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 HuaweiBrowser/11.0.3.304 Safari/537.36", @@ -494,6 +1079,33 @@ "type": "tablet" } }, + { + "desc": "Huawei MatePad T10s", + "ua": "Mozilla/5.0 (Linux; U; Android 10; zh-cn; AGS3-AL00 Build/HUAWEIAGS3-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/11.4 Mobile Safari/537.36 COVC/045530", + "expect": { + "vendor": "Huawei", + "model": "AGS3-AL00", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T10s WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 10; AGS3-W09 Build/HUAWEIAGS3-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.93 Safari/537.36 OPR/60.0.2254.59405", + "expect": { + "vendor": "Huawei", + "model": "AGS3-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T8 8 LTE", + "ua": "Mozilla/5.0 (Linux; U; Android 10; KOB2K-L09 Build/HUAWEIKOB2K-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36 OPR/83.0.2254.73002", + "expect": { + "vendor": "Huawei", + "model": "KOB2K-L09", + "type": "tablet" + } + }, { "desc": "Huawei M3", "ua": "Mozilla/5.0 (Linux; Android 7.0; BTV-W09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", @@ -944,6 +1556,15 @@ "type": "mobile" } }, + { + "desc": "Infinix XPad", + "ua": "Mozilla/5.0 (Linux; Android 14; Infinix X1101B Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.99 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", + "expect": { + "vendor": "Infinix", + "model": "X1101B", + "type": "tablet" + } + }, { "desc": "Infinix Zero 5G", "ua": "Mozilla/5.0 (Linux; Android 12; Infinix X6815B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", From 29b76368fd5a28a2e703d83b3f28d919233a6800 Mon Sep 17 00:00:00 2001 From: Harald Reingruber <74898239+haraldreingruber-dedalus@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:11:31 +0100 Subject: [PATCH 278/388] Add missing 1.0.39 changes to CHANGELOG.md (#766) --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0be492a1e..5545bafac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -166,6 +166,14 @@ --- +## Version 0.7.39 / 1.0.39 +- Add new feature: executable command using `npx ua-parser-js "[INSERT-UA-HERE]"` +- Add new browser: Helio, Pico Browser, Wolvic +- Add new device vendor: itel, Nothing, TCL +- Improve browser detection: ICEBrowser, Klar, QQBrowser, Quark, Rekonq, Sleipnir +- Improve device detection: Xiaomi Pro, Amazon Echo Show, Samsung Galaxy Watch +- Removed from browser: Viera + ## Version 0.7.38 / 1.0.38 - Fix error on getOS() when userAgentData.platform is undefined - Add new browser: Opera GX, Twitter @@ -251,4 +259,4 @@ Version 1.0.x is basically the equivalent of version 0.7.x (mirror/duplicate). S ## Version 0.8 -Version 0.8 was created by accident. This version is now deprecated and no longer maintained, please update to version 0.7 / 1.0. \ No newline at end of file +Version 0.8 was created by accident. This version is now deprecated and no longer maintained, please update to version 0.7 / 1.0. From 4c57a5a5672c98faf8fc8efbc81de6753ce7f1dd Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 20 Nov 2024 16:44:41 +0700 Subject: [PATCH 279/388] Improve device detection for tablets: OnePlus, Xiaomi --- src/main/ua-parser.js | 14 +-- test/specs/device-all.json | 171 +++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 6 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 21021a558..e915b24d6 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -76,6 +76,7 @@ LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', + OPPO = 'OPPO', SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', @@ -542,6 +543,10 @@ ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ // Xiaomi + /oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i, + /\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets + ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ + /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi @@ -549,16 +554,13 @@ /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i // Xiaomi Mi ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ - /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad - /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets - ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ // OPPO /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i - ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ - /\b(opd2\d{3}a?) bui/i - ], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, OPPO], [TYPE, MOBILE]], [ + /\b(opd2(\d{3}a?))(?: bui|\))/i + ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [ // Vivo /vivo (\w+)(?: bui|\))/i, diff --git a/test/specs/device-all.json b/test/specs/device-all.json index ce131ef8f..e0f2aeee3 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2375,6 +2375,33 @@ "type": "mobile" } }, + { + "desc": "OnePlus Pad Go 11.35", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; OPD2304) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.629 YaApp_Android/24.101/apad YaSearchBrowser/24.101/apad BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2304", + "type": "tablet" + } + }, + { + "desc": "OnePlus Pad 2 12.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; OPD2403 Build/UKQ1.231108.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2403", + "type": "tablet" + } + }, + { + "desc": "OnePlus Pad 11.61 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; OPD2203 Build/UKQ1.230924.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2203", + "type": "tablet" + } + }, { "desc": "OPPO Pad", "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; OPD2101 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", @@ -4175,6 +4202,15 @@ "type": "mobile" } }, + { + "desc": "Xiaomi MI PAD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.4; en-us; MI PAD Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/9.3.2", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD", + "type": "tablet" + } + }, { "desc": "Xiaomi MI PAD 2", "ua": "Mozilla/5.0 (Linux; Android 5.1; MI PAD 2 Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Safari/537.36 [FB_IAB/FB4A;FBAV/137.0.0.24.91;]", @@ -4184,6 +4220,33 @@ "type": "tablet" } }, + { + "desc": "Xiaomi MI PAD 2", + "ua": "Mozilla/5.0 (Linux; x86_64; Android 5.1; MI PAD 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 YaBrowser/20.11.2.69.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 2", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 3", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.0; MI PAD 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.216 YaBrowser/21.5.6.56.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 3", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 4", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 8.1.0; MI PAD 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 YaBrowser/19.9.1.126.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 4", + "type": "tablet" + } + }, { "desc": "Xiaomi MI PAD 4 PLUS", "ua": "Mozilla/5.0 (Linux; Android 8.1; MI PAD 4 PLUS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36", @@ -4193,6 +4256,69 @@ "type": "tablet" } }, + { + "desc": "Xiaomi MI PAD 4 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 8.1; Mi Pad4 Wi-Fi) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Mobile Safari/537.36 EdgA/86.0.622.61", + "expect": { + "vendor": "Xiaomi", + "model": "Mi Pad4 Wi-Fi", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5", + "ua": "Mozilla/5.0 (Linux; Android 13; 21051182G Build/TKQ1.221013.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Xiaomi", + "model": "21051182G", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; M2105K81AC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Safari/537.36 Line/13.15.1/IAB", + "expect": { + "vendor": "Xiaomi", + "model": "M2105K81AC", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; M2105K81C) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2105K81C", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 6 Max 14", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-tw; 2307BRPDCC Build/UKQ1.230804.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.118 Mobile Safari/537.36 Device/yudi Model/2307BRPDCC XiaoMi/MiuiBrowser/14.10.6", + "expect": { + "vendor": "Xiaomi", + "model": "2307BRPDCC", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 6 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 13; en-US; 23046RP50C Build/TKQ1.221114.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.6.2.1316 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "23046RP50C", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Pad 6S Pro 12.4", + "ua": "Mozilla/5.0 (Linux; Android 14; 24018RPACC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "24018RPACC", + "type": "tablet" + } + }, { "desc": "Xiaomi POCO X2", "ua": "Mozilla/5.0 (Linux; Android 10; POCO X2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", @@ -4364,6 +4490,51 @@ "type": "mobile" } }, + { + "desc": "XiaoMi Redmi Pad", + "ua": "Mozilla/5.0 (Linux; U; Android 12; id-id; Redmi Pad Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36 XiaoMi/MiuiBrowser/14.1.1-gn", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Pad", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad", + "ua": "Mozilla/5.0 (Linux; Android 14; 22081283G Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Xiaomi", + "model": "22081283G", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; 2405CRPFDG Build/UKQ1.240116.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.97 Safari/537.36 [FB_IAB/FB4A;FBAV/476.0.0.49.74;] FBNV/1", + "expect": { + "vendor": "Xiaomi", + "model": "2405CRPFDG", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad SE", + "ua": "Dalvik/2.1.0 (Linux; U; Android 14; 23073RPBFG Build/UKQ1.231003.002)", + "expect": { + "vendor": "Xiaomi", + "model": "23073RPBFG", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad SE 8.7", + "ua": "Mozilla/5.0 (Linux; Android 14; 24076RP19G Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Mobile Safari/537.36 Line/14.18.1/IAB", + "expect": { + "vendor": "Xiaomi", + "model": "24076RP19G", + "type": "tablet" + } + }, { "desc": "ZTE Blade A6", "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE BLADE A0620 Build/NMF26F; ru-ru) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36 Puffin/9.2.0.50586AP", From 536031d78977a7af78996f82cf5be75a049650c3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 20 Nov 2024 16:59:37 +0700 Subject: [PATCH 280/388] Improve device detection: Google Pixel Tablet --- src/main/ua-parser.js | 2 +- test/specs/device-all.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index e915b24d6..b0bf17973 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -598,7 +598,7 @@ ], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [ // Google - /(pixel c)\b/i // Google Pixel C + /(pixel (c|tablet))\b/i // Google Pixel C/Tablet ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ diff --git a/test/specs/device-all.json b/test/specs/device-all.json index e0f2aeee3..b45d4ac8f 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -4760,6 +4760,15 @@ "type": "mobile" } }, + { + "desc": "Google Pixel Tablet", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel Tablet Build/AP2A.240905.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel Tablet", + "type": "tablet" + } + }, { "desc": "Google Pixel XL", "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", From 4ddbeeaa069dd6b0dee81e542c0c0ce1e6dd1fe1 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 20 Nov 2024 17:01:04 +0700 Subject: [PATCH 281/388] Insert a link to contributing guidelines in PR template --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- CONTRIBUTING.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f1a3b8dba..c658b1889 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ # Prerequisites -- [ ] I have read and follow the contributing guidelines +- [ ] I have read and follow the [contributing](https://github.com/faisalman/ua-parser-js/blob/master/CONTRIBUTING.md) guidelines - [ ] I have read and accept the [Contributor License Agreement (CLA)](https://gist.github.com/faisalman/2ed16621ebb544157eba85a7f7381417) Document and I hereby sign the CLA # Type of Change diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7ac0d654..0bb377bc7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,4 +4,4 @@ * Make some changes as required * Write unit test to showcase its functionality under `/test` * Run the test suites to make sure it's not breaking anything `$ npm run build+test` -* Submit a pull request under `develop` branch & check the CLA in the submission form \ No newline at end of file +* Submit a pull request & check the CLA in the submission form \ No newline at end of file From c8ece08b1b90131746db6b02aba114aa2bf2c5f7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 20 Nov 2024 17:19:46 +0700 Subject: [PATCH 282/388] Improve device detection: Google Pixel Watch --- src/main/ua-parser.js | 3 ++- test/specs/device-all.json | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b0bf17973..2fb34d3a8 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -782,7 +782,8 @@ /\b(sm-[lr]\d\d[05][fnuw]?s?)\b/i // Samsung Galaxy Watch ], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [ - /((pebble))app/i // Pebble + /((pebble))app/i, // Pebble + /(google) (pixel watch[\w ]*)( bui|\))/i // Pixel Watch ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ diff --git a/test/specs/device-all.json b/test/specs/device-all.json index b45d4ac8f..9adbd0949 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -4769,6 +4769,24 @@ "type": "tablet" } }, + { + "desc": "Google Pixel Watch", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch Build/TWD4.231005.002)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch", + "type": "wearable" + } + }, + { + "desc": "Google Pixel Watch 2", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch 2 Build/TWD9.240605.001.A1)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch 2", + "type": "wearable" + } + }, { "desc": "Google Pixel XL", "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", From c72198d47032d4535f01a1d98fd8858f9cc9bd4c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 20 Nov 2024 17:54:41 +0700 Subject: [PATCH 283/388] Improve device detection: Nokia tablets --- src/main/ua-parser.js | 6 ++++-- test/specs/device-all.json | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 2fb34d3a8..39c8d7bb1 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -593,9 +593,11 @@ ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ // Nokia + /(nokia) (t[12][01])/i + ], [VENDOR, MODEL, [TYPE, TABLET]], [ /(?:maemo|nokia).*(n900|lumia \d+)/i, - /nokia[-_ ]?([-\w\.]*)/i - ], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [ + /nokia[-_ ]?(([-\w\.]*))/i + ], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [ // Google /(pixel (c|tablet))\b/i // Google Pixel C/Tablet diff --git a/test/specs/device-all.json b/test/specs/device-all.json index 9adbd0949..d73fd3e95 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -2186,6 +2186,33 @@ "type": "mobile" } }, + { + "desc": "Nokia T20", + "ua": "Mozilla/5.0 (Linux; Android 12; Nokia T20) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "T20", + "type": "tablet" + } + }, + { + "desc": "Nokia T20", + "ua": "Mozilla/5.0 (Linux; Android 11; Nokia T20 Build/RP1A.201005.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/93.0.4577.62 Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "T20", + "type": "tablet" + } + }, + { + "desc": "Nokia T21", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Nokia T21 Build/TP1A.220624.014)", + "expect": { + "vendor": "Nokia", + "model": "T21", + "type": "tablet" + } + }, { "desc": "Nokia 2720 Flip", "ua": "Mozilla/5.0 (Mobile; Nokia_2720_Flip; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5.2", From 94d3d84cd55caf6b46514e4be175b8ca9d612d73 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 20 Nov 2024 18:00:18 +0700 Subject: [PATCH 284/388] Add new device vendor: HMD --- src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 2 +- test/specs/device-all.json | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ab4629668..37066fca7 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -213,6 +213,7 @@ const Vendor = Object.freeze({ GEEKSPHONE: 'GeeksPhone', GENERIC: 'Generic', GOOGLE: 'Google', + HMD: 'HMD', HP: 'HP', HTC: 'HTC', HUAWEI: 'Huawei', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 39c8d7bb1..a3b867e62 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -697,7 +697,7 @@ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (imo) ([\w ]+?)(?: bui|\))/i, // IMO + /; (hmd|imo) ([\w ]+?)(?: bui|\))/i, // HMD/IMO /(hp) ([\w ]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i, // Asus /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia diff --git a/test/specs/device-all.json b/test/specs/device-all.json index d73fd3e95..bfefe5d1c 100644 --- a/test/specs/device-all.json +++ b/test/specs/device-all.json @@ -350,6 +350,33 @@ "type": "mobile" } }, + { + "desc": "HMD Pulse", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse", + "type": "mobile" + } + }, + { + "desc": "HMD Pulse Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse Plus) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse Plus", + "type": "mobile" + } + }, + { + "desc": "HMD Pulse Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse Pro", + "type": "mobile" + } + }, { "desc": "Honor MagicPad 13 WiFi", "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; GDI-W09 Build/HONORGDI-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", From a1d81c928c3776d8906280ab2fc95e7f41323481 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 21 Nov 2024 16:06:27 +0700 Subject: [PATCH 285/388] Move device test spec into separate files --- test/mocha-test.js | 10 +- test/specs/device-all.json | 5362 ---------------------------- test/specs/devices/_others.json | 250 ++ test/specs/devices/acer.json | 11 + test/specs/devices/advan.json | 29 + test/specs/devices/alcatel.json | 20 + test/specs/devices/amazon.json | 137 + test/specs/devices/apple.json | 128 + test/specs/devices/asus.json | 155 + test/specs/devices/blackberry.json | 47 + test/specs/devices/cat.json | 38 + test/specs/devices/energizer.json | 29 + test/specs/devices/facebook.json | 38 + test/specs/devices/fairphone.json | 29 + test/specs/devices/google.json | 236 ++ test/specs/devices/hmd.json | 29 + test/specs/devices/honor.json | 164 + test/specs/devices/htc.json | 29 + test/specs/devices/huawei.json | 947 +++++ test/specs/devices/imo.json | 38 + test/specs/devices/infinix.json | 56 + test/specs/devices/itel.json | 56 + test/specs/devices/jolla.json | 11 + test/specs/devices/kobo.json | 20 + test/specs/devices/lenovo.json | 74 + test/specs/devices/lg.json | 164 + test/specs/devices/meizu.json | 20 + test/specs/devices/micromax.json | 30 + test/specs/devices/microsoft.json | 47 + test/specs/devices/motorola.json | 83 + test/specs/devices/nintendo.json | 47 + test/specs/devices/nokia.json | 74 + test/specs/devices/nothing.json | 29 + test/specs/devices/nvidia.json | 11 + test/specs/devices/oneplus.json | 119 + test/specs/devices/oppo.json | 137 + test/specs/devices/ouya.json | 11 + test/specs/devices/panasonic.json | 47 + test/specs/devices/pico.json | 29 + test/specs/devices/polytron.json | 38 + test/specs/devices/realme.json | 74 + test/specs/devices/roku.json | 29 + test/specs/devices/samsung.json | 371 ++ test/specs/devices/sharp.json | 56 + test/specs/devices/smartfren.json | 29 + test/specs/devices/sony.json | 209 ++ test/specs/devices/tcl.json | 479 +++ test/specs/devices/technisat.json | 20 + test/specs/devices/tecno.json | 20 + test/specs/devices/tesla.json | 20 + test/specs/devices/ulefone.json | 92 + test/specs/devices/vivo.json | 74 + test/specs/devices/xiaomi.json | 524 +++ test/specs/devices/zte.json | 11 + 54 files changed, 5474 insertions(+), 5363 deletions(-) delete mode 100644 test/specs/device-all.json create mode 100644 test/specs/devices/_others.json create mode 100644 test/specs/devices/acer.json create mode 100644 test/specs/devices/advan.json create mode 100644 test/specs/devices/alcatel.json create mode 100644 test/specs/devices/amazon.json create mode 100644 test/specs/devices/apple.json create mode 100644 test/specs/devices/asus.json create mode 100644 test/specs/devices/blackberry.json create mode 100644 test/specs/devices/cat.json create mode 100644 test/specs/devices/energizer.json create mode 100644 test/specs/devices/facebook.json create mode 100644 test/specs/devices/fairphone.json create mode 100644 test/specs/devices/google.json create mode 100644 test/specs/devices/hmd.json create mode 100644 test/specs/devices/honor.json create mode 100644 test/specs/devices/htc.json create mode 100644 test/specs/devices/huawei.json create mode 100644 test/specs/devices/imo.json create mode 100644 test/specs/devices/infinix.json create mode 100644 test/specs/devices/itel.json create mode 100644 test/specs/devices/jolla.json create mode 100644 test/specs/devices/kobo.json create mode 100644 test/specs/devices/lenovo.json create mode 100644 test/specs/devices/lg.json create mode 100644 test/specs/devices/meizu.json create mode 100644 test/specs/devices/micromax.json create mode 100644 test/specs/devices/microsoft.json create mode 100644 test/specs/devices/motorola.json create mode 100644 test/specs/devices/nintendo.json create mode 100644 test/specs/devices/nokia.json create mode 100644 test/specs/devices/nothing.json create mode 100644 test/specs/devices/nvidia.json create mode 100644 test/specs/devices/oneplus.json create mode 100644 test/specs/devices/oppo.json create mode 100644 test/specs/devices/ouya.json create mode 100644 test/specs/devices/panasonic.json create mode 100644 test/specs/devices/pico.json create mode 100644 test/specs/devices/polytron.json create mode 100644 test/specs/devices/realme.json create mode 100644 test/specs/devices/roku.json create mode 100644 test/specs/devices/samsung.json create mode 100644 test/specs/devices/sharp.json create mode 100644 test/specs/devices/smartfren.json create mode 100644 test/specs/devices/sony.json create mode 100644 test/specs/devices/tcl.json create mode 100644 test/specs/devices/technisat.json create mode 100644 test/specs/devices/tecno.json create mode 100644 test/specs/devices/tesla.json create mode 100644 test/specs/devices/ulefone.json create mode 100644 test/specs/devices/vivo.json create mode 100644 test/specs/devices/xiaomi.json create mode 100644 test/specs/devices/zte.json diff --git a/test/mocha-test.js b/test/mocha-test.js index 68213ac9b..9b35330eb 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -7,10 +7,18 @@ var traverse = require('@babel/traverse').default; var {UAParser} = require('../src/main/ua-parser'); var browsers = require('./specs/browser-all.json'); var cpus = require('./specs/cpu-all.json'); -var devices = require('./specs/device-all.json'); +var devices = readJsonFiles('test/specs/devices'); var engines = require('./specs/engine-all.json'); var os = require('./specs/os-all.json'); +function readJsonFiles(dir) { + var list = []; + fs.readdirSync(dir).forEach(function (file) { + list.push(...JSON.parse(fs.readFileSync(`${dir}/${file}`, 'utf-8'))); + }); + return list; +}; + var parser = new UAParser(); var methods = [ { diff --git a/test/specs/device-all.json b/test/specs/device-all.json deleted file mode 100644 index bfefe5d1c..000000000 --- a/test/specs/device-all.json +++ /dev/null @@ -1,5362 +0,0 @@ -[ - { - "desc": "K", - "ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "undefined", - "model": "K", - "type": "mobile" - } - }, - { - "desc": "Advan M4", - "ua": "Mozilla/5.0 (Linux; U; Android 6.0; ADVAN M4 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36 OPR/28.0.2254.119214", - "expect": { - "vendor": "ADVAN", - "model": "M4", - "type": "mobile" - } - }, - { - "desc": "Advan S40", - "ua": "Mozilla/5.0 (Linux; Android 7.0; ADVAN S40 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Mobile Safari/537.36 EdgA/79.0.309.58", - "expect": { - "vendor": "ADVAN", - "model": "S40", - "type": "mobile" - } - }, - { - "desc": "Advan Sketsa 2", - "ua": "Mozilla/5.0 (Linux; Android 11; ADVAN 1011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Safari/537.36", - "expect": { - "vendor": "ADVAN", - "model": "1011", - "type": "mobile" - } - }, - { - "desc": "ASUS Nexus 7", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "Nexus 7", - "type": "tablet" - } - }, - { - "desc": "ASUS Padfone", - "ua": "Mozilla/5.0 (Linux; Android 4.1.1; PadFone 2 Build/JRO03L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "PadFone", - "type": "tablet" - } - }, - { - "desc": "ASUS ZenPad 10", - "ua": "Mozilla/5.0 (Linux; Android 6.0; P00C Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "P00C", - "type": "tablet" - } - }, - { - "desc": "ASUS ZenPad Z8s", - "ua": "Mozilla/5.0 (Linux; Android 7.0; ASUS_P00J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Safari/537.36\n", - "expect": { - "vendor": "ASUS", - "model": "P00J", - "type": "tablet" - } - }, - { - "desc": "ASUS ROG", - "ua": "Mozilla/5.0 (Linux; Android 8.1; ZS600KL Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "ZS600KL", - "type": "mobile" - } - }, - { - "desc": "ASUS ROG II", - "ua": "Mozilla/5.0 (Linux; Android 9; ASUS_I001DA Build/PKQ1.190414.001; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "I001DA", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone 2", - "ua": "Mozilla/5.0 (Linux; Android 5.0; ASUS ZenFone 2 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "ZenFone 2", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone 3 Deluxe", - "ua": "Mozilla/5.0 (Linux; Android 6.0; ASUS_Z016D Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.132 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "Z016D", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone 5", - "ua": "Mozilla/5.0 (Linux; Android 8.0; ZE620KL Build/OPR1.170623.032) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.158 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "ZE620KL", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone 7", - "ua": "Mozilla/5.0 (Linux; Android 10; ASUS_I002D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.81 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "I002D", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone 7 Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; ZS671KS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "ZS671KS", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone Max Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; ZB602KL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "ZB602KL", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone Max Pro (M1)", - "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X00TD Build/OPM1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "X00TD", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone Max M2", - "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X01AD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "X01AD", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone Max Pro M2", - "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X01BDA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "X01BDA", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone Go", - "ua": "Mozilla/5.0 (Linux; Android 6.0; ASUS_X009DA Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "X009DA", - "type": "mobile" - } - }, - { - "desc": "ASUS Zenfone 2 Laser", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; ASUS_Z00ED) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "ASUS", - "model": "Z00ED", - "type": "mobile" - } - }, - { - "desc": "Acer Iconia A1-810", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; A1-810 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36", - "expect": { - "vendor": "Acer", - "model": "A1-810", - "type": "tablet" - } - }, - { - "desc": "BlackBerry Priv", - "ua": "User-Agent: Mozilla/5.0 (Linux; Android 5.1.1; STV100-1 Build/LMY47V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/46.0.2490.76 Mobile Safari/537.36", - "expect": { - "vendor": "BlackBerry", - "model": "STV100-1", - "type": "mobile" - } - }, - { - "desc": "BlackBerry Keyone", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BBB100-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.111 Mobile Safari/537.36", - "expect": { - "vendor": "BlackBerry", - "model": "BBB100-1", - "type": "mobile" - } - }, - { - "desc": "BlackBerry Key2", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BBF100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36", - "expect": { - "vendor": "BlackBerry", - "model": "BBF100-1", - "type": "mobile" - } - }, - { - "desc": "BlackBerry Key2 LE", - "ua": "User-Agent: Mozilla/5.0 (Linux; Android 8.1.0; BBE100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497 Mobile Safari/537.36", - "expect": { - "vendor": "BlackBerry", - "model": "BBE100-1", - "type": "mobile" - } - }, - { - "desc": "Blackview 4900Pro", - "ua": "Mozilla/5.0 (Linux; Android 12; BV4900Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "undefined", - "model": "BV4900Pro", - "type": "mobile" - } - }, - { - "desc": "Cat B15Q", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; B15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36", - "expect": { - "vendor": "Cat", - "model": "B15Q", - "type": "mobile" - } - }, - { - "desc": "Cat B35", - "ua": "Mozilla/5.0 (Mobile; CAT B35; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5.1", - "expect": { - "vendor": "Cat", - "model": "B35", - "type": "mobile" - } - }, - { - "desc": "Cat S22 Flip", - "ua": "Mozilla/5.0 (Linux; Android 11; S22 FLIP Build/RKQ1.210416.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.165 Mobile Safari/537.36", - "expect": { - "vendor": "Cat", - "model": "S22 FLIP", - "type": "mobile" - } - }, - { - "desc": "Cat S62 Pro", - "ua": "Mozilla/5.0 (Linux; Android 11; S62 Pro Build/RKQ1.210406.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/94.0.4606.85 Mobile Safari/537.36 GSA/12.34.17.23.arm64", - "expect": { - "vendor": "Cat", - "model": "S62 Pro", - "type": "mobile" - } - }, - { - "desc": "Desktop (IE11 with Tablet string)", - "ua": "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; GWX:MANAGED; rv:11.0) like Gecko", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "undefined" - } - }, - { - "desc": "Mobile (DuckDuckGo mobile browser)", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile DuckDuckGo/5 Safari/537.36", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "mobile" - } - }, - { - "desc": "Energizer Energy 400", - "ua": "Mozilla/5.0 (Linux; Android 6.0; Energy400 Build/MRA58K test-keys; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.158 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/172.0.0.66.93;]", - "expect": { - "vendor": "Energizer", - "model": "Energy400", - "type": "mobile" - } - }, - { - "desc": "Energizer Energy 400S", - "ua": "Mozilla/5.0 (Linux; Android 6.0; Energy 400S Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.85 Mobile Safari/537.36", - "expect": { - "vendor": "Energizer", - "model": "Energy 400S", - "type": "mobile" - } - }, - { - "desc": "Energizer Ultimate 65G", - "ua": "Mozilla/5.0 (Linux; Android 14; Energizer Ultimate 65G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Energizer", - "model": "Ultimate 65G", - "type": "mobile" - } - }, - { - "desc": "Fairphone 1U", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; FP1U Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "Fairphone", - "model": "FP1U", - "type": "mobile" - } - }, - { - "desc": "Fairphone 2", - "ua": "Mozilla/5.0 (Linux; Android 7.1.2; FP2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", - "expect": { - "vendor": "Fairphone", - "model": "FP2", - "type": "mobile" - } - }, - { - "desc": "Fairphone 3", - "ua": "Mozilla/5.0 (Linux; Android 9; FP3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", - "expect": { - "vendor": "Fairphone", - "model": "FP3", - "type": "mobile" - } - }, - { - "desc": "HMD Pulse", - "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "HMD", - "model": "Pulse", - "type": "mobile" - } - }, - { - "desc": "HMD Pulse Plus", - "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse Plus) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "HMD", - "model": "Pulse Plus", - "type": "mobile" - } - }, - { - "desc": "HMD Pulse Pro", - "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "HMD", - "model": "Pulse Pro", - "type": "mobile" - } - }, - { - "desc": "Honor MagicPad 13 WiFi", - "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; GDI-W09 Build/HONORGDI-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "GDI-W09", - "type": "tablet" - } - }, - { - "desc": "Honor Pad 2", - "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; en-nz; JDN-W09 Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/6.0 Mobile Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "JDN-W09", - "type": "tablet" - } - }, - { - "desc": "Honor Pad 2", - "ua": "Mozilla/5.0 (Linux; U; Android 9; zh-Hans-CN; JDN2-W09HN Build/HUAWEIJDN2-W09HN) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/4.6.6.164 Mobile Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "JDN2-W09HN", - "type": "tablet" - } - }, - { - "desc": "Honor Pad 7 10.1", - "ua": "Mozilla/5.0 (Linux; Android 12; AGM3-AL09HN Build/HONORAGM3-AL09HN; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.46 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/490.0.0.63.82;IABMV/1;]", - "expect": { - "vendor": "Honor", - "model": "AGM3-AL09HN", - "type": "tablet" - } - }, - { - "desc": "Honor Pad 8 12.0", - "ua": "Mozilla/5.0 (Linux; Android 12; HEY-W09 Build/HONORHEY-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "HEY-W09", - "type": "tablet" - } - }, - { - "desc": "Honor Pad 9 12.1", - "ua": "Mozilla/5.0 (Linux; Android 13; HEY2-N09 Build/HONORHEY2-N09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 [FB_IAB/FB4A;FBAV/465.0.0.63.83;]", - "expect": { - "vendor": "Honor", - "model": "HEY2-N09", - "type": "tablet" - } - }, - { - "desc": "Honor Pad 9 12.1 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 14; HEY2-W09 Build/HONORHEY2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.102 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", - "expect": { - "vendor": "Honor", - "model": "HEY2-W09", - "type": "tablet" - } - }, - { - "desc": "Honor Pad V7 Pro 11", - "ua": "Mozilla/5.0 (Linux; Android 12; BRT-AN09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 EdgA/109.0.1518.53", - "expect": { - "vendor": "Honor", - "model": "BRT-AN09", - "type": "tablet" - } - }, - { - "desc": "Honor Pad V7 Pro 11 WiFi", - "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; BRT-W09 Build/HONORBRT-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.5.0.336 Mobile Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "BRT-W09", - "type": "tablet" - } - }, - { - "desc": "Honor Pad X6", - "ua": "Mozilla/5.0 (Linux; Android 10; AGR-W09HN Build/HUAWEIAGR-W09HN; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.9 SP-engine/2.28.0 baiduboxapp/12.9.0.11 (Baidu; P1 10) NABar/1.0", - "expect": { - "vendor": "Honor", - "model": "AGR-W09HN", - "type": "tablet" - } - }, - { - "desc": "Honor Pad X7 8 LTE", - "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-AL00HN; HMSCore 6.0.0.306) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.3.300 Mobile Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "KOB2-AL00HN", - "type": "tablet" - } - }, - { - "desc": "Honor Pad X7 8 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-W09HN; HMSCore 6.1.0.314) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.0.0.301 Mobile Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "KOB2-W09HN", - "type": "tablet" - } - }, - { - "desc": "Honor Pad X8 Lite", - "ua": "Mozilla/5.0 (Linux; Android 12; AGM-W09HN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "AGM-W09HN", - "type": "tablet" - } - }, - { - "desc": "Honor Pad X9 11.5 LTE", - "ua": "Mozilla/5.0 (Linux; Android 13; ELN-L09 Build/HONORELN-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Mobile Safari/537.36[FBAN/EMA;FBLC/zh_CN;FBAV/432.0.0.9.110;FBCX/modulariab;]", - "expect": { - "vendor": "Honor", - "model": "ELN-L09", - "type": "tablet" - } - }, - { - "desc": "Honor Pad X9 11.5 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 13; ELN-W09 Build/HONORELN-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "ELN-W09", - "type": "tablet" - } - }, - { - "desc": "HTC Desire 820", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; HTC Desire 820 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36", - "expect": { - "vendor": "HTC", - "model": "Desire 820", - "type": "mobile" - } - }, - { - "desc": "HTC Evo Shift 4G", - "ua": "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Sprint APA7373KT Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0", - "expect": { - "vendor": "Sprint", - "model": "APA7373KT", - "type": "mobile" - } - }, - { - "desc": "HTC Nexus 9", - "ua": "Mozilla/5.0 (Linux; Android 5.0; Nexus 9 Build/LRX21R) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Mobile Crosswalk/7.36.154.13 Safari/537.36", - "expect": { - "vendor": "HTC", - "model": "Nexus 9", - "type": "tablet" - } - }, - { - "desc": "Huawei Honor", - "ua": "Mozilla/5.0 (Linux; U; Android 2.3; xx-xx; U8860 Build/HuaweiU8860) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", - "expect": { - "vendor": "Huawei", - "model": "U8860", - "type": "mobile" - } - }, - { - "desc": "Huawei Honor 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L41) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YAL-L41", - "type": "mobile" - } - }, - { - "desc": "Huawei Honor 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; YAL-AL10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YAL-AL10", - "type": "mobile" - } - }, - { - "desc": "Huawei Nexus 6P", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 6P Build/MTC19V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Mobile Safari/537", - "expect": { - "vendor": "Huawei", - "model": "Nexus 6P", - "type": "mobile" - } - }, - { - "desc": "Huawei P10", - "ua": "Mozilla/5.0 (Linux; Android 7.0; VTR-L09 Build/HUAWEIVTR-L09; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "VTR-L09", - "type": "mobile" - } - }, - { - "desc": "Huawei Y3II", - "ua": "Mozilla/5.0 (Linux; U; Android 5.1; xx-xx; HUAWEI LUA-L03 Build/HUAWEILUA-L03) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LUA-L03", - "type": "mobile" - } - }, - { - "desc": "HUAWEI MediaPad C5 8", - "ua": "Mozilla/5.0 (Linux; Android 7.0; MON-AL19B Build/HUAWEIMON-AL19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.83 Mobile Safari/537.36 T7/11.7 baiduboxapp/11.7.0.10 (Baidu; P1 7.0)", - "expect": { - "vendor": "Huawei", - "model": "MON-AL19B", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M2 10.1", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; HUAWEI M2-A01L Build/HUAWEIM2-A01L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/95.0.4638.74 Safari/537.36[FBAN/EMA;FBLC/fr_FR;FBAV/421.0.0.14.100;]", - "expect": { - "vendor": "Huawei", - "model": "M2-A01L", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M3", - "ua": "Mozilla/5.0 (Linux; U; Android 6.0; en-US; BTV-DL09 Build/HUAWEIBEETHOVEN-DL09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 UCBrowser/11.5.0.1015 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BTV-DL09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M3 8", - "ua": "Mozilla/5.0 (Linux; Android 7.0; HUAWEI BTV-W09 Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.96 Mobile Safari/537.36 AlohaBrowser/3.1.1", - "expect": { - "vendor": "Huawei", - "model": "BTV-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M3 Lite", - "ua": "Mozilla/5.0 (Linux; Android 7.0; CPN-L09 Build/HUAWEICPN-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36[FBAN/EMA;FBLC/ru_RU;FBAV/233.0.0.12.118;]", - "expect": { - "vendor": "Huawei", - "model": "CPN-L09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M3 Lite", - "ua": "Mozilla/5.0 (Linux; Android 7.0; CPN-W09 Build/HUAWEICPN-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.71 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/374.0.0.20.109;]", - "expect": { - "vendor": "Huawei", - "model": "CPN-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M3 Lite 10", - "ua": "Mozilla/5.0 (Linux; Android 7.0; BAH-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BAH-L09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M5 10.8", - "ua": "Mozilla/5.0 (Linux; Android 9; CMR-W09 Build/HUAWEICMR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.102 Safari/537.36 Line/14.18.1/IAB", - "expect": { - "vendor": "Huawei", - "model": "CMR-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M5 Lite", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; BAH2-W19 Build/HUAWEIBAH2-W19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BAH2-W19", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M5 Lite", - "ua": "Mozilla/5.0 (Linux; Android 9; JDN2-W09 Build/HUAWEIJDN2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.210 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/318.0.0.39.154;]", - "expect": { - "vendor": "Huawei", - "model": "JDN2-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M5 Lite", - "ua": "Mozilla/5.0 (Linux; Android 9; JDN2-AL50 Build/HUAWEIJDN2-AL50; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.13.0 SP-engine/2.29.0 matrixstyle/0 lite baiduboxapp/5.8.0.10 (Baidu; P1 9) NABar/1.", - "expect": { - "vendor": "Huawei", - "model": "JDN2-AL50", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M5 8.4", - "ua": "Mozilla/5.0 (Linux; Android 9; SHT-W09 Build/HUAWEISHT-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.87 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "SHT-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M5", - "ua": "Mozilla/5.0 (Linux; Android 9; SHT-AL09 Build/HUAWEISHT-AL09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "SHT-AL09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M6 10.8", - "ua": "Mozilla/5.0 (Linux; Android 14; SCM-W09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6612.143 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "SCM-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad M6 8.4", - "ua": "Mozilla/5.0 (Linux; Android 9; VRD-W09; HMSCore 6.14.0.321; GMSCore 22.26.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "VRD-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T5", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; AGS2-L09 Build/HUAWEIAGS2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.125 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGS2-L09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T10", - "ua": "Mozilla/5.0 (Linux; U; Android 10; en-US; AGR-L09 Build/HUAWEIAGR-L09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.3.8.1305 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGR-L09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T10", - "ua": "Mozilla/5.0 (Linux; Android 10; AGR-W09 Build/HUAWEIAGR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGR-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T10s", - "ua": "Mozilla/5.0 (Linux; Android 10; AGS3-W09 Build/HUAWEIAGS3-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGS3-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T 8.0", - "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-L09 Build/HUAWEIKOB2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/396.0.0.21.104;]", - "expect": { - "vendor": "Huawei", - "model": "KOB2-L09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T 8.0", - "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-W09 Build/HUAWEIKOB2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 Mobile Safari/537.36 HuaweiBrowser/15.0.4.312 HMSCore/6.14.0.301", - "expect": { - "vendor": "Huawei", - "model": "KOB2-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T1 10", - "ua": "Mozilla/5.0 (Linux; Android 4.4.4; T1-A21w Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Safari/537.36 SputnikBrowser/1.2.8.161", - "expect": { - "vendor": "Huawei", - "model": "T1-A21w", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T1 10", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; T1-A23L Build/HuaweiMediaPad; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.121 Mobile Safari/537.36 BingWeb/6.9.10", - "expect": { - "vendor": "Huawei", - "model": "T1-A23L", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T1 10", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; T1-A21L Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "T1-A21L", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T1 7", - "ua": "Mozilla/5.0 (Linux; 4.4.2; T1-701u) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "T1-701u", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T1 8", - "ua": "Mozilla/5.0 (Linux; U; Android 9.0; MediaPad T1 8.0 Build/HuaweiMediaPad) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 OPR/28.0.2254.119224", - "expect": { - "vendor": "Huawei", - "model": "MediaPad T1 8.0", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T10 9.7", - "ua": "Mozilla/5.0 (Linux; U; Android 10; en-US; AGRK-L09 Build/HUAWEIAGRK-L09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.6.0.1315 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGRK-L09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T10 9.7 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 10; AGRK-W09; HMSCore 6.14.0.321) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGRK-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T10s 10.1 LTE", - "ua": "Mozilla/5.0 (Linux; Android 10; AGS3K-L09 Build/HUAWEIAGS3K-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36 [FB_IAB/FB4A;FBAV/362.0.0.27.109;]", - "expect": { - "vendor": "Huawei", - "model": "AGS3K-L09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T10s 10.1 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 10; AGS3K-W09; HMSCore 6.14.0.321) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGS3K-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T2 10.0 Pro", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; 605HW Build/HuaweiMediaPad; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.5005.78 Safari/537.36 [FB_IAB/FB4A;FBAV/436.0.0.35.101;]", - "expect": { - "vendor": "Huawei", - "model": "605HW", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T2 7.0 Pro", - "ua": "Mozilla/5.0 (Linux; Android 6.0; BGO-DL09 Build/HuaweiBAGGIO; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/407.0.0.30.97;]", - "expect": { - "vendor": "Huawei", - "model": "BGO-DL09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T3 10", - "ua": "Mozilla/5.0 (Linux; Android 7.0; AGS-W09 Build/HUAWEIAGS-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.90 Safari/537.36 GSA/10.83.10.21.arm64", - "expect": { - "vendor": "Huawei", - "model": "AGS-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T3 7", - "ua": "Mozilla/5.0 (Linux; Android 7.0; BG2-U03 Build/HUAWEIBG2-U03; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.111 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BG2-U03", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T3 8", - "ua": "Mozilla/5.0 (Linux; Android 7.0; KOB-W09 Build/HUAWEIKOB-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Safari/537.36 [FB_IAB/Orca-Android;FBAV/354.0.0.10.113;]", - "expect": { - "vendor": "Huawei", - "model": "KOB-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad T5 10", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; AGS2-W09 Build/HUAWEIAGS2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", - "expect": { - "vendor": "Huawei", - "model": "AGS2-W09", - "type": "tablet" - } - }, - { - "desc": "HUAWEI MediaPad X2", - "ua": "Mozilla/5.0 (Linux; Android 8.0; GEM-703L Build/HUAWEIGEM-703L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/043906 Mobile Safari/537.36 MicroMessenger/6.6.3.1260(0x26060339) NetType/WIFI Language/zh_", - "expect": { - "vendor": "Huawei", - "model": "GEM-703L", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 10.4", - "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH3-W09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BAH3-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 10.4", - "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH3-L09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BAH3-L09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 10.4 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 10; BAH3-W59 Build/HUAWEIBAH3-W59; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36HiSearch/22.0.6.315", - "expect": { - "vendor": "Huawei", - "model": "BAH3-W59", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 10.4 (2022)", - "ua": "Mozilla/5.0 (Linux; Android 10; BAH4-L09 Build/HUAWEIBAH4-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BAH4-L09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 10.4 (2022) WiFi", - "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH4-W09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BAH4-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 10.4 SE", - "ua": "Mozilla/5.0 (Linux; Android 12; AGS5-L09 Build/HUAWEIAGS5-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGS5-L09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 10.4 SE WiFi", - "ua": "Mozilla/5.0 (Linux; Android 12; AGS5-W09 Build/HUAWEIAGS5-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Safari/537.36 [FB_IAB/FB4A;FBAV/480.0.0.54.88;]", - "expect": { - "vendor": "Huawei", - "model": "AGS5-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 11 (2023) WiFi", - "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; DBR-W10 Build/HUAWEIDBR-W10) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.9.6.501 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "DBR-W10", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 11 WiFi", - "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-cn; DBY-W09 Build/HUAWEIDBY-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.86 MQQBrowser/14.6 Mobile Safari/537.36 COVC/046801", - "expect": { - "vendor": "Huawei", - "model": "DBY-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 11.5 Air WiFi", - "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; DBY2-W00 Build/HUAWEIDBY2-W00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/7.3.8.663 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "DBY2-W00", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 11.5 LTE", - "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; BTK-AL09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BTK-AL09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 11.5 S WiFi", - "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; TGR-W09; HMSCore 6.14.0.322; GMSCore 0.3.3.1.240913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 HuaweiBrowser/14.0.2.317 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "TGR-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad 11.5 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; BTK-W09; HMSCore 6.14.0.322; GMSCore 214816056) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BTK-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad C5 8", - "ua": "Mozilla/5.0 (Linux; Android 7.0; MON-W19 Build/HUAWEIMON-W19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36 [Pinterest/Android]", - "expect": { - "vendor": "Huawei", - "model": "MON-W19", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad Pro 11", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; GOT-AL09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 YaBrowser/23.5.5.60.01 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "GOT-AL09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad Pro 11 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 12; GOT-W09 Build/HUAWEIGOT-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Safari/537.36 T7/13.19 BDOS/1.0 (HarmonyOS 3.0.0) SP-engine/2.57.0 baiduboxapp/13.19.0.12 (Baidu; P1 12) NABar/1.0", - "expect": { - "vendor": "Huawei", - "model": "GOT-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad Pro 12.6 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 10; WGR-W09 Build/HUAWEIWGR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/412.0.0.8.106;]", - "expect": { - "vendor": "Huawei", - "model": "WGR-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad SE 11 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; AGS6-W09; HMSCore 6.12.2.309) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.5.315 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGS6-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad Pro 13.2", - "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; PCE-W29; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "PCE-W29", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad T 10", - "ua": "Mozilla/5.0 (Linux; Android 10; AGR-L09; HMSCore 5.0.4.301) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 HuaweiBrowser/11.0.3.304 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AGR-L09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad T10s", - "ua": "Mozilla/5.0 (Linux; U; Android 10; zh-cn; AGS3-AL00 Build/HUAWEIAGS3-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/11.4 Mobile Safari/537.36 COVC/045530", - "expect": { - "vendor": "Huawei", - "model": "AGS3-AL00", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad T10s WiFi", - "ua": "Mozilla/5.0 (Linux; U; Android 10; AGS3-W09 Build/HUAWEIAGS3-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.93 Safari/537.36 OPR/60.0.2254.59405", - "expect": { - "vendor": "Huawei", - "model": "AGS3-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei MatePad T8 8 LTE", - "ua": "Mozilla/5.0 (Linux; U; Android 10; KOB2K-L09 Build/HUAWEIKOB2K-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36 OPR/83.0.2254.73002", - "expect": { - "vendor": "Huawei", - "model": "KOB2K-L09", - "type": "tablet" - } - }, - { - "desc": "Huawei M3", - "ua": "Mozilla/5.0 (Linux; Android 7.0; BTV-W09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BTV-W09", - "type": "tablet" - } - }, - { - "desc": "Huawei Mate 10 Pro", - "ua": "Mozilla/5.0 (Linux; Android 8.0; BLA-L29 Build/HUAWEIBLA-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3236.6 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BLA-L29", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate X", - "ua": "Mozilla/5.0 (Linux; Android 9; TAH-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.111 Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "TAH-AN00", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate X2", - "ua": "Mozilla/5.0 (Linux; Android 10; TET-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "TET-AN00", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 20 X", - "ua": "Mozilla/5.0 (Linux; Android 9; EVR-L29 Build/HUAWEIEVR-L29; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "EVR-L29", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LYA-L09", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LYA-AL00", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LYA-AL10", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L0C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LYA-L0C", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LYA-L29", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; LYA-TL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LYA-TL00", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 50 Pro", - "ua": "Mozilla/5.0 (Linux; Android 12; DCO-LX9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "DCO-LX9", - "type": "mobile" - } - }, - { - "desc": "Huawei P20 Lite", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "ANE-LX1", - "type": "mobile" - } - }, - { - "desc": "Huawei P20", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; EML-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "EML-L29", - "type": "mobile" - } - }, - { - "desc": "Huawei P20 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; CLT-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "CLT-L29", - "type": "mobile" - } - }, - { - "desc": "Huawei P30", - "ua": "Mozilla/5.0 (Linux; Android 9; ELE-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "ELE-L29", - "type": "mobile" - } - }, - { - "desc": "Huawei P30 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; VOG-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "VOG-L29", - "type": "mobile" - } - }, - { - "desc": "Huawei P40", - "ua": "Mozilla/5.0 (Linux; Android 10; ANA-AN00 Build/HUAWEIANA-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/11.26 SP-engine/2.22.0 baiduboxapp/11.26.0.10 (Baidu; P1 10) NABar/1.0", - "expect": { - "vendor": "Huawei", - "model": "ANA-AN00", - "type": "mobile" - } - }, - { - "desc": "Huawei P40 Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; ELS-AN00 Build/HUAWEIELS-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36 mailapp/6.0.0", - "expect": { - "vendor": "Huawei", - "model": "ELS-AN00", - "type": "mobile" - } - }, - { - "desc": "Huawei 30 Pro+", - "ua": "Mozilla/5.0 (Linux; Android 10; EBG-AN10 Build/HUAWEIEBG-AN10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.86 Mobile Safari/537.36 EdgA/42.0.0.2741", - "expect": { - "vendor": "Huawei", - "model": "EBG-AN10", - "type": "mobile" - } - }, - { - "desc": "Huawei 30S", - "ua": "Mozilla/5.0 (Linux; Android 10; CDY-AN90 Build/HUAWEICDY-AN90; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36 mailapp/5.8.0", - "expect": { - "vendor": "Huawei", - "model": "CDY-AN90", - "type": "mobile" - } - }, - { - "desc": "Huawei Nova 5T", - "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YAL-L21", - "type": "mobile" - } - }, - { - "desc": "Huawei Nova 5T", - "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YAL-L61", - "type": "mobile" - } - }, - { - "desc": "Huawei Nova 5T", - "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L71) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YAL-L71", - "type": "mobile" - } - }, - { - "desc": "Huawei Nova 5T", - "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YAL-L61D", - "type": "mobile" - } - }, - { - "desc": "Huawei Nova 5T", - "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YALE-L61A", - "type": "mobile" - } - }, - { - "desc": "Huawei Nova 5T", - "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YALE-L61D", - "type": "mobile" - } - }, - { - "desc": "Huawei Nova 5T", - "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L71A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "YALE-L71A", - "type": "mobile" - } - }, - { - "desc": "Huawei Enjoy10e", - "ua": "Dalvik/2.1.0 (Linux; U; Android 10; MED-AL00 Build/HUAWEIMED-AL00)", - "expect": { - "vendor": "Huawei", - "model": "MED-AL00", - "type": "mobile" - } - }, - { - "desc": "Huawei Honor 6A", - "ua": "Mozilla/5.0 (Linux; Android 7.0; DLI-L22 Build/HONORDLI-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/252.0.0.22.355;]", - "expect": { - "vendor": "Honor", - "model": "DLI-L22", - "type": "mobile" - } - }, - { - "desc": "Huawei Honor 7", - "ua": "Mozilla/5.0 (Linux; Android 6.0; PLK-L01 Build/HONORPLK-L01; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "PLK-L01", - "type": "mobile" - } - }, - { - "desc": "Huawei 10 Lite", - "ua": "Mozilla/5.0 (Linux; Android 9; HRY-LX1 Build/HONORHRY-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36", - "expect": { - "vendor": "Honor", - "model": "HRY-LX1", - "type": "mobile" - } - }, - { - "desc": "Huawei Y7 2018", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; LDN-L01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "LDN-L01", - "type": "mobile" - } - }, - { - "desc": "Huawei Honor 8X", - "ua": "Mozilla/5.0 (Linux; Android 9; JSN-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "JSN-L21", - "type": "mobile" - } - }, - { - "desc": "Huawei Y6 2019", - "ua": "Mozilla/5.0 (Linux; Android 9; MRD-LX1N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "MRD-LX1N", - "type": "mobile" - } - }, - { - "desc": "Huawei Y9 2019", - "ua": "Mozilla/5.0 (Linux; Android 9; JKM-LX2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "JKM-LX2", - "type": "mobile" - } - }, - { - "desc": "Huawei Y5", - "ua": "Mozilla/5.0 (Linux; Android 9; AMN-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "AMN-LX3", - "type": "mobile" - } - }, - { - "desc": "Huawei Y7p", - "ua": "Mozilla/5.0 (Linux; Android 9; ART-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "ART-L29", - "type": "mobile" - } - }, - { - "desc": "Huawei Mate 20 Lite", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; SNE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.116 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "SNE-LX1", - "type": "mobile" - } - }, - { - "desc": "Huawei P10 Lite", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; WAS-LX1A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "WAS-LX1A", - "type": "mobile" - } - }, - { - "desc": "Huawei Y5 Lite 2018", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; DRA-LX5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "DRA-LX5", - "type": "mobile" - } - }, - { - "desc": "Huawei Honor 8C", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BKK-LX2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", - "expect": { - "vendor": "Huawei", - "model": "BKK-LX2", - "type": "mobile" - } - }, - { - "desc": "IMO FEEL A2", - "ua": "Mozilla/5.0 (Linux; Android 5.1; IMO FEEL A2 Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/49.0.2623.105 Mobile Safari/537.36", - "expect": { - "vendor": "IMO", - "model": "FEEL A2", - "type": "mobile" - } - }, - { - "desc": "IMO Q2", - "ua": "Mozilla/5.0 (Linux; Android 5.1; IMO Q2 Build/LMY47D; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Mobile Safari/537.36 GSA/7.22.24.21.arm", - "expect": { - "vendor": "IMO", - "model": "Q2", - "type": "mobile" - } - }, - { - "desc": "IMO S2", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; IMO S2 Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.162 Mobile Safari/537.36", - "expect": { - "vendor": "IMO", - "model": "S2", - "type": "mobile" - } - }, - { - "desc": "IMO Tab X9", - "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; id-id; IMO TAB X9 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", - "expect": { - "vendor": "IMO", - "model": "TAB X9", - "type": "tablet" - } - }, - { - "desc": "Infinix Hot 7 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; Infinix X625C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Infinix", - "model": "X625C", - "type": "mobile" - } - }, - { - "desc": "Infinix Hot 10T", - "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X689C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Infinix", - "model": "X689C", - "type": "mobile" - } - }, - { - "desc": "Infinix Hot 11s", - "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X6812 Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.116 Mobile Safari/537.36", - "expect": { - "vendor": "Infinix", - "model": "X6812", - "type": "mobile" - } - }, - { - "desc": "Infinix Smart 5", - "ua": "Mozilla/5.0 (Linux; Android 10; Infinix X657C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Infinix", - "model": "X657C", - "type": "mobile" - } - }, - { - "desc": "Infinix XPad", - "ua": "Mozilla/5.0 (Linux; Android 14; Infinix X1101B Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.99 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", - "expect": { - "vendor": "Infinix", - "model": "X1101B", - "type": "tablet" - } - }, - { - "desc": "Infinix Zero 5G", - "ua": "Mozilla/5.0 (Linux; Android 12; Infinix X6815B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Infinix", - "model": "X6815B", - "type": "mobile" - } - }, - { - "desc": "Apple Desktop", - "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15", - "expect": { - "vendor": "Apple", - "model": "Macintosh", - "type": "undefined" - } - }, - { - "desc": "Apple Watch", - "ua": "atc/1.0 watchOS/7.3.3 model/Watch4,2 hwp/t8006 build/18S830 (6; dt:191)", - "expect": { - "vendor": "Apple", - "model": "watch", - "type": "wearable" - } - }, - { - "desc": "iPad using UCBrowser", - "ua": "Mozilla/5.0 (iPad; U; CPU OS 11_2 like Mac OS X; zh-CN; iPad5,3) AppleWebKit/534.46 (KHTML, like Gecko) UCBrowser/3.0.1.776 U3/ Mobile/10A403 Safari/7543.48.3", - "expect": { - "vendor": "Apple", - "model": "iPad", - "type": "tablet" - } - }, - { - "desc": "iPad Air", - "ua": "Mozilla/5.0 (iPad; CPU OS 12_4_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPad4,1;FBMD/iPad;FBSN/iOS;FBSV/12.4.5;FBSS/2;FBID/tablet;FBLC/en_US;FBOP/5;FBCR/]", - "expect": { - "vendor": "Apple", - "model": "iPad", - "type": "tablet" - } - }, - { - "desc": "iPad using Facebook Browser", - "ua": "Mozilla/5.0 (iPad; CPU OS 14_4_2 like Mac OS X) WebKit/8610 (KHTML, like Gecko) Mobile/18D70 [FBAN/FBIOS;FBDV/iPad7,11;FBMD/iPad;FBSN/iOS;FBSV/14.4.2;FBSS/2;FBID/tablet;FBLC/en_US;FBOP/5]", - "expect": { - "vendor": "Apple", - "model": "iPad", - "type": "tablet" - } - }, - { - "desc": "iPod", - "ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53", - "expect": { - "vendor": "Apple", - "model": "iPod touch", - "type": "mobile" - } - }, - { - "desc": "JVC LT-43V55LFA Smart TV", - "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB211 VSTVB MB200 HbbTV/1.2.1 (; JVC; MB211; 3.19.4.2; _TV_NT72563_2017 SmartTvA/3.0.0", - "expect": { - "vendor": "JVC", - "model": "MB211", - "type": "smarttv" - } - }, - { - "desc": "JVC LT-43V65LUA Smart TV", - "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB130 VSTVB MB100 HbbTV/1.2.1 (; JVC; MB130; 5.7.20.0; _TV_G10_2017;) SmartTvA/3.0.0", - "expect": { - "vendor": "JVC", - "model": "MB130", - "type": "smarttv" - } - }, - { - "desc": "Kobo eReader", - "ua": "Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Kobo eReader Safari/538.1", - "expect": { - "vendor": "Kobo", - "model": "eReader", - "type": "tablet" - } - }, - { - "desc": "Kobo Touch", - "ua": "Mozilla/5.0 (Linux; U; Android 2.0; en-us;) AppleWebKit/538.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/538.1 (Kobo Touch 0377/4.20.14622)", - "expect": { - "vendor": "Kobo", - "model": "Touch", - "type": "tablet" - } - }, - { - "desc": "Lenovo Tab 2", - "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36", - "expect": { - "vendor": "Lenovo", - "model": "TAB 2 A7", - "type": "tablet" - } - }, - { - "desc": "Lenovo Phone", - "ua": "Mozilla/5.0 (Linux; Android 6.0; Lenovo PB2-650M Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/311.0.0.44.117;]", - "expect": { - "vendor": "Lenovo", - "model": "PB2-650M", - "type": "mobile" - } - }, - { - "desc": "Lenovo Tab 3 Pro", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo YT3-X90F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", - "expect": { - "vendor": "Lenovo", - "model": "YT3-X90F", - "type": "tablet" - } - }, - { - "desc": "Lenovo Tab 4", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Lenovo TB-X304F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", - "expect": { - "vendor": "Lenovo", - "model": "TB-X304F", - "type": "tablet" - } - }, - { - "desc": "Lenovo Tab 4", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Lenovo TAB 2 A7-30HC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", - "expect": { - "vendor": "Lenovo", - "model": "TAB 2 A7", - "type": "tablet" - } - }, - { - "desc": "Lenovo Tab 7 Essential", - "ua": "Mozilla/5.0 (Linux; Android 7.0; Lenovo TB-7304X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", - "expect": { - "vendor": "Lenovo", - "model": "TB-7304X", - "type": "tablet" - } - }, - { - "desc": "Lenovo Tab M10", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X606F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 YaBrowser/20.9.4.99.01 Safari/537.36", - "expect": { - "vendor": "Lenovo", - "model": "TB-X606F", - "type": "tablet" - } - }, - { - "desc": "Lenovo IdeaTab S6000", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; IdeaTab S6000-H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 YaBrowser/18.11.1.1011.01 Safari/537.36", - "expect": { - "vendor": "Lenovo", - "model": "IdeaTab S6000-H", - "type": "tablet" - } - }, - { - "desc": "LG V40 ThinQ", - "ua": "Mozilla/5.0 (Linux; Android 9; LM-V405) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", - "expect": { - "vendor": "LG", - "model": "LM-V405", - "type": "mobile" - } - }, - { - "desc": "LG K30", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; LM-X410.F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "LG", - "model": "LM-X410.F", - "type": "mobile" - } - }, - { - "desc": "LG K30", - "ua": "Mozilla/5.0 (Linux; Android 9; LM-X410.FGN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", - "expect": { - "vendor": "LG", - "model": "LM-X410.FGN", - "type": "mobile" - } - }, - { - "desc": "LG K40", - "ua": "Mozilla/5.0 (Linux; Android 10; LM-X420) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36", - "expect": { - "vendor": "LG", - "model": "LM-X420", - "type": "mobile" - } - }, - { - "desc": "LG Stylo 4", - "ua": "Mozilla/5.0 (Linux; Android 10; LM-Q710(FGN)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36", - "expect": { - "vendor": "undefined", - "model": "LM-Q710(FGN)", - "type": "mobile" - } - }, - { - "desc": "LG Stylo 5", - "ua": "Mozilla/5.0 (Linux; Android 9; LM-Q720) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", - "expect": { - "vendor": "LG", - "model": "LM-Q720", - "type": "mobile" - } - }, - { - "desc": "LG G7 ThinQ", - "ua": "Mozilla/5.0 (Linux; Android 9; LM-G710VM Build/PKQ1.181105.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.136 Mobile Safari/537.36", - "expect": { - "vendor": "LG", - "model": "LM-G710VM", - "type": "mobile" - } - }, - { - "desc": "LG K20", - "ua": "Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:111.0) Gecko/111.0 Firefox/111.0", - "expect": { - "vendor": "LG", - "model": "M255", - "type": "mobile" - } - }, - { - "desc": "LG K500", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; LG-K500 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36", - "expect": { - "vendor": "LG", - "model": "K500", - "type": "mobile" - } - }, - { - "desc": "LG Nexus 4", - "ua": "Mozilla/5.0 (Linux; Android 4.2.1; Nexus 4 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19", - "expect": { - "vendor": "LG", - "model": "Nexus 4", - "type": "mobile" - } - }, - { - "desc": "LG Nexus 4", - "ua": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; Google Nexus 4 - 4.3 - API 18 - 768x1280 Build/JLS36G) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "LG", - "model": "Nexus 4", - "type": "mobile" - } - }, - { - "desc": "LG Nexus 5", - "ua": "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19", - "expect": { - "vendor": "LG", - "model": "Nexus 5", - "type": "mobile" - } - }, - { - "desc": "LG Wing", - "ua": "Mozilla/5.0 (Linux; Android 10; LM-F100N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.101 Mobile Safari/537.36", - "expect": { - "vendor": "LG", - "model": "LM-F100N", - "type": "mobile" - } - }, - { - "desc": "LG Smart TV", - "ua": "Mozilla/5.0 (DirectFB; U; Linux mips; en) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) LG Browser (; LG NetCast.TV-2011)", - "expect": { - "vendor": "LG", - "model": "undefined", - "type": "smarttv" - } - }, - { - "desc": "LG Smart TV", - "ua": "Mozilla/5.0 (Linux; NetCast; U) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/53.0.2785 34 Safari/537.31 SmartTV/8.5", - "expect": { - "vendor": "LG", - "model": "undefined", - "type": "smarttv" - } - }, - { - "desc": "LG Android TV", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; LG Android TV Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", - "expect": { - "vendor": "LG", - "model": "undefined", - "type": "smarttv" - } - }, - { - "desc": "Loewe Smart TV", - "ua": "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", - "expect": { - "vendor": "LOEWE", - "model": "SL410", - "type": "smarttv" - } - }, - { - "desc": "Meizu M5 Note", - "ua": "Mozilla/5.0 (Linux; Android 6.0; M5 Note Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043024 Safari/537.36 MicroMessenger/6.5.7.1040 NetType/WIFI Language/zh_CN", - "expect": { - "vendor": "Meizu", - "model": "M5 Note", - "type": "mobile" - } - }, - { - "desc": "Micromax Bharat 2 Plus", - "ua": "Mozilla/5.0 (Linux; U; Android 7.0; en-US; Micromax Q402Plus Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.12.9.1226 Mobile Safari/537.36", - "expect": { - "vendor": "Micromax", - "model": "Q402Plus", - "type": "mobile" - } - }, - { - "desc": "Micromax Canvas Infinity", - "ua": "Mozilla/5.0 (Linux; U; Android 7.1.2; en-US; Micromax HS2 Build/N2G47H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/13.2.0.1296 (SpeedMode) U4/1.0 UCWEB/2.0 Mobile Safari/534.30", - "expect": { - "vendor": "Micromax", - "model": "HS2", - "type": "mobile" - } - }, - { - "desc": "Micromax In 1b", - "ua": "Mozilla/5.0 (Linux; U; Android 10; Micromax E7533 Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 OPR/54.0.2254.56148", - "expect": { - "vendor": "Micromax", - "model": "E7533", - "type": "mobile" - } - }, - { - "desc": "Microsoft Lumia 950", - "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", - "expect": { - "vendor": "Microsoft", - "model": "Lumia 950", - "type": "mobile" - } - }, - { - "desc": "Microsoft Surface Duo", - "ua": "Dalvik/2.1.0 (Linux; U; Android 10; Surface Duo Build/2020.1014.61)", - "expect": { - "vendor": "Microsoft", - "model": "Surface Duo", - "type": "tablet" - } - }, - { - "desc": "Motorola Moto X", - "ua": "Mozilla/5.0 (Linux; Android 4.4.4; XT1097 Build/KXE21.187-38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", - "expect": { - "vendor": "Motorola", - "model": "XT1097", - "type": "mobile" - } - }, - { - "desc": "Motorola Moto Z3 Play", - "ua": "Mozilla/5.0 (Linux; Android 9; Moto Z3 Play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Motorola", - "model": "Moto Z3 Play", - "type": "mobile" - } - }, - { - "desc": "Meizu M3S", - "ua": "Mozilla/5.0 (X11; Linux; Android 5.1; MZ-M3s Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrom/45.0.2454.94 Mobile Safari/537.36", - "expect": { - "vendor": "Meizu", - "model": "M3s", - "type": "mobile" - } - }, - { - "desc": "Microsoft Lumia 950", - "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", - "expect": { - "vendor": "Microsoft", - "model": "Lumia 950", - "type": "mobile" - } - }, - { - "desc": "Motorola Nexus 6", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.20 Mobile Safari/537.36", - "expect": { - "vendor": "Motorola", - "model": "Nexus 6", - "type": "mobile" - } - }, - { - "desc": "Motorola Droid RAZR 4G", - "ua": "Mozilla/5.0 (Linux; U; Android 2.3; xx-xx; DROID RAZR 4G Build/6.5.1-73_DHD-11_M1-29) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", - "expect": { - "vendor": "Motorola", - "model": "DROID RAZR 4G", - "type": "mobile" - } - }, - { - "desc": "Motorola RAZR 2019", - "ua": "Mozilla/5.0 (Linux; Android 9; motorola razr) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.1 Chrome/75.0.3770.143 Mobile Safari/537.36", - "expect": { - "vendor": "Motorola", - "model": "razr", - "type": "mobile" - } - }, - { - "desc": "iPhone", - "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53", - "expect": { - "vendor": "Apple", - "model": "iPhone", - "type": "mobile" - } - }, - { - "desc": "iPhone SE", - "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", - "expect": { - "vendor": "Apple", - "model": "iPhone", - "type": "mobile" - } - }, - { - "desc": "iPhone SE using Facebook App", - "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", - "expect": { - "vendor": "Apple", - "model": "iPhone", - "type": "mobile" - } - }, - { - "desc": "iPhone 11 Pro Max", - "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone12,5;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", - "expect": { - "vendor": "Apple", - "model": "iPhone", - "type": "mobile" - } - }, - { - "desc": "iPhone XS", - "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone11,2;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", - "expect": { - "vendor": "Apple", - "model": "iPhone", - "type": "mobile" - } - }, - { - "desc": "iPod touch", - "ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A501 Safari/9537.53", - "expect": { - "vendor": "Apple", - "model": "iPod touch", - "type": "mobile" - } - }, - { - "desc": "itel A25", - "ua": "Mozilla/5.0 (Linux; Android 9; itel L5002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.130 Mobile Safari/537.36 OPR/63.3.3216.58675", - "expect": { - "vendor": "itel", - "model": "L5002", - "type": "mobile" - } - }, - { - "desc": "itel A50", - "ua": "Mozilla/5.0 (Linux; U; Android 14; itel A667L Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Mobile Safari/537.36 OPR/83.1.2254.73239", - "expect": { - "vendor": "itel", - "model": "A667L", - "type": "mobile" - } - }, - { - "desc": "itel KidPad 1", - "ua": "Mozilla/5.0 (Linux; Android 10; Itel W7001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.101 Mobile Safari/537.36", - "expect": { - "vendor": "itel", - "model": "W7001", - "type": "tablet" - } - }, - { - "desc": "itel Pad One", - "ua": "Mozilla/5.0 (Linux; Android 12; itel P10001L Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.172 Safari/537.36", - "expect": { - "vendor": "itel", - "model": "P10001L", - "type": "tablet" - } - }, - { - "desc": "itel RS4", - "ua": "Mozilla/5.0 (Linux; Android 13; itel S666LN Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.165 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/468.1.0.56.78;]", - "expect": { - "vendor": "itel", - "model": "S666LN", - "type": "mobile" - } - }, - { - "desc": "itel Vision 2S", - "ua": "Mozilla/5.0 (Linux; Android 11; itel P651L Build/RP1A.201005.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 Mobile Safari/537.36", - "expect": { - "vendor": "itel", - "model": "P651L", - "type": "mobile" - } - }, - { - "desc": "Moto X", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "Motorola", - "model": "XT1058", - "type": "mobile" - } - }, - { - "desc": "Motorola Moto g(6) Play", - "ua": "Mozilla/5.0 (Linux; Android 9; moto g(6) play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", - "expect": { - "vendor": "Motorola", - "model": "moto g(6) play", - "type": "mobile" - } - }, - { - "desc": "Motorola Moto g(7) Supra", - "ua": "Mozilla/5.0 (Linux; Android 9; moto g(7) supra Build/PCOS29.114-134-2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/73.0.3683.90 Mobile Safari/537.36", - "expect": { - "vendor": "Motorola", - "model": "moto g(7) supra", - "type": "mobile" - } - }, - { - "desc": "Motorola Moto E", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Build/NDQS26.69-64-11-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", - "expect": { - "vendor": "Motorola", - "model": "Moto E (4)", - "type": "mobile" - } - }, - { - "desc": "Nokia3xx", - "ua": "Nokia303/14.87 CLDC-1.1", - "expect": { - "vendor": "Nokia", - "model": "303", - "type": "mobile" - } - }, - { - "desc": "Nokia 3.2", - "ua": "Mozilla/5.0 (Linux; Android 10; Nokia 3.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36", - "expect": { - "vendor": "Nokia", - "model": "3.2", - "type": "mobile" - } - }, - { - "desc": "Nokia 7", - "ua": "Mozilla/5.0 (Linux; Android 11; Nokia 7.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Nokia", - "model": "7.2", - "type": "mobile" - } - }, - { - "desc": "Nokia N9", - "ua": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", - "expect": { - "vendor": "Nokia", - "model": "N9", - "type": "mobile" - } - }, - { - "desc": "Nokia T20", - "ua": "Mozilla/5.0 (Linux; Android 12; Nokia T20) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", - "expect": { - "vendor": "Nokia", - "model": "T20", - "type": "tablet" - } - }, - { - "desc": "Nokia T20", - "ua": "Mozilla/5.0 (Linux; Android 11; Nokia T20 Build/RP1A.201005.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/93.0.4577.62 Safari/537.36", - "expect": { - "vendor": "Nokia", - "model": "T20", - "type": "tablet" - } - }, - { - "desc": "Nokia T21", - "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Nokia T21 Build/TP1A.220624.014)", - "expect": { - "vendor": "Nokia", - "model": "T21", - "type": "tablet" - } - }, - { - "desc": "Nokia 2720 Flip", - "ua": "Mozilla/5.0 (Mobile; Nokia_2720_Flip; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5.2", - "expect": { - "vendor": "Nokia", - "model": "2720 Flip", - "type": "mobile" - } - }, - { - "desc": "Nothing 1", - "ua": "Mozilla/5.0 (Linux; Android 13; A063) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Mobile Safari/537.36", - "expect": { - "vendor": "Nothing", - "model": "A063", - "type": "mobile" - } - }, - { - "desc": "Nothing 2", - "ua": "Mozilla/5.0 (Linux; Android 14; A065 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.53 Mobile Safari/537.36", - "expect": { - "vendor": "Nothing", - "model": "A065", - "type": "mobile" - } - }, - { - "desc": "Nothing 2a", - "ua": "Mozilla/5.0 (Linux; Android 14; A142 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.71 Mobile Safari/537.36", - "expect": { - "vendor": "Nothing", - "model": "A142", - "type": "mobile" - } - }, - { - "desc": "Oculus Quest", - "ua": "Mozilla/5.0 (Linux; Android 10; Quest) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/15.0.0.0.22.280317669 SamsungBrowser/4.0 Chrome/89.0.4389.90 VR Safari/537.36", - "expect": { - "vendor": "Facebook", - "model": "Quest", - "type": "xr" - } - }, - { - "desc": "Oculus Quest 2", - "ua": "Mozilla/5.0 (Linux; Android 10; Quest 2) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/15.0.0.0.22.280317669 SamsungBrowser/4.0 Chrome/89.0.4389.90 VR Safari/537.36", - "expect": { - "vendor": "Facebook", - "model": "Quest 2", - "type": "xr" - } - }, - { - "desc": "Oculus Quest 3", - "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest 3) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/31.4.0.6.51.566757996 Chrome/120.0.6099.283 VR Safari/537.36", - "expect": { - "vendor": "Facebook", - "model": "Quest 3", - "type": "xr" - } - }, - { - "desc": "Oculus Quest Pro", - "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest Pro) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/24.4.0.22.60.426469926 SamsungBrowser/4.0 Chrome/106.0.5249.181 VR Safari/537.36", - "expect": { - "vendor": "Facebook", - "model": "Quest Pro", - "type": "xr" - } - }, - { - "desc": "Issue #747", - "ua": "python-requests/2.25.1", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "undefined" - } - }, - { - "desc": "OnePlus One", - "ua": "Mozilla/5.0 (Linux; Android 4.4.4; A0001 Build/KTU84Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.59 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "A0001", - "type": "mobile" - } - }, - { - "desc": "OnePlus One", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; OnePlus One A0001 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "A0001", - "type": "mobile" - } - }, - { - "desc": "OnePlus 2", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; ONE A2003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "A2003", - "type": "mobile" - } - }, - { - "desc": "OnePlus 3", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ONEPLUS A3000 Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "A3000", - "type": "mobile" - } - }, - { - "desc": "OnePlus 6", - "ua": "Mozilla/5.0 (Linux; Android 9; ONEPLUS A6003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "A6003", - "type": "mobile" - } - }, - { - "desc": "OnePlus 6T", - "ua": "Mozilla/5.0 (Linux; Android 9; ONEPLUS A6010) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "A6010", - "type": "mobile" - } - }, - { - "desc": "OnePlus 7T Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36 EdgA/110.0.1587.66", - "expect": { - "vendor": "undefined", - "model": "HD1913", - "type": "mobile" - } - }, - { - "desc": "OnePlus 8T", - "ua": "Mozilla/5.0 (Linux; Android 11; KB2005) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "KB2005", - "type": "mobile" - } - }, - { - "desc": "OnePlus 8 Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; IN2025) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.119 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "IN2025", - "type": "mobile" - } - }, - { - "desc": "OnePlus 10RT", - "ua": "Mozilla/5.0 (Linux; Android 13; CPH2413) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "CPH2413", - "type": "mobile" - } - }, - { - "desc": "OnePlus Nord N100", - "ua": "Mozilla/5.0 (Linux; Android 10; BE2015 Build/QKQ1.200719.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "BE2015", - "type": "mobile" - } - }, - { - "desc": "OnePlus Nord N10 5G", - "ua": "Mozilla/5.0 (Linux; Android 10; BE2029) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.185 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "BE2029", - "type": "mobile" - } - }, - { - "desc": "OnePlus Pad Go 11.35", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; OPD2304) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.629 YaApp_Android/24.101/apad YaSearchBrowser/24.101/apad BroPP/1.0 SA/3 Mobile Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "OPD2304", - "type": "tablet" - } - }, - { - "desc": "OnePlus Pad 2 12.1 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 14; OPD2403 Build/UKQ1.231108.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "OPD2403", - "type": "tablet" - } - }, - { - "desc": "OnePlus Pad 11.61 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 14; OPD2203 Build/UKQ1.230924.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", - "expect": { - "vendor": "OnePlus", - "model": "OPD2203", - "type": "tablet" - } - }, - { - "desc": "OPPO Pad", - "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; OPD2101 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "OPD2101", - "type": "tablet" - } - }, - { - "desc": "OPPO Neo", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; R831T Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 OppoBrowser/3.3.2 Mobile Safari/534.30", - "expect": { - "vendor": "OPPO", - "model": "R831T", - "type": "mobile" - } - }, - { - "desc": "OPPO R7s", - "ua": "Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; OPPO R7s Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/7.1 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "R7s", - "type": "mobile" - } - }, - { - "desc": "OPPO A3s", - "ua": "Mozilla/5.0 (Linux; Android 8.1; CPH1803 Build/OPM1.171019.026; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "CPH1803", - "type": "mobile" - } - }, - { - "desc": "OPPO A12", - "ua": "Mozilla/5.0 (Linux; Android 9; CPH2083) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "CPH2083", - "type": "mobile" - } - }, - { - "desc": "OPPO Reno", - "ua": "Mozilla/5.0 (Linux; Android 9; PCAT00 Build/PKQ1.190101.001; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "PCAT00", - "type": "mobile" - } - }, - { - "desc": "OPPO Reno3 Pro 5G", - "ua": "Mozilla/5.0 (Linux; Android 10; PCLM50) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.117 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "PCLM50", - "type": "mobile" - } - }, - { - "desc": "OPPO Reno4 SE", - "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; PEAM00 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "PEAM00", - "type": "mobile" - } - }, - { - "desc": "OPPO Reno4 5G", - "ua": "Mozilla/5.0 (Linux; Android 10; PDPM00 Build/QKQ1.200216.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "PDPM00", - "type": "mobile" - } - }, - { - "desc": "OPPO Reno4 Pro 5G", - "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; PDNT00 Build/QKQ1.200216.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "PDNT00", - "type": "mobile" - } - }, - { - "desc": "OPPO Reno5 A", - "ua": "Mozilla/5.0 (Linux; Android 11; A101OP) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "A101OP", - "type": "mobile" - } - }, - { - "desc": "OPPO Find X", - "ua": "Mozilla/5.0 (Linux; Android 8.1; PAFM00 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "PAFM00", - "type": "mobile" - } - }, - { - "desc": "OPPO Find 7a", - "ua": "Mozilla/5.0 (Linux; U; Android 4.3; xx-xx; X9007 Build/JLS36C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "OPPO", - "model": "X9007", - "type": "mobile" - } - }, - { - "desc": "OPPO F5", - "ua": "ozilla/5.0 (Linux; Android 7.1.1; CPH1723) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "CPH1723", - "type": "mobile" - } - }, - { - "desc": "Realme C1", - "ua": "Mozilla/5.0 (Linux; Android 8.1; RMX1811 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Mobile Safari/537.36", - "expect": { - "vendor": "Realme", - "model": "RMX1811", - "type": "mobile" - } - }, - { - "desc": "Realme C2", - "ua": "Mozilla/5.0 (Linux; Android 9; RMX1941) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", - "expect": { - "vendor": "Realme", - "model": "RMX1941", - "type": "mobile" - } - }, - { - "desc": "Realme Narzo 20", - "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; RMX2193 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", - "expect": { - "vendor": "Realme", - "model": "RMX2193", - "type": "mobile" - } - }, - { - "desc": "Realme 2 Pro", - "ua": "Mozilla/5.0 (Linux; Android 9; RMX1801) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", - "expect": { - "vendor": "Realme", - "model": "RMX1801", - "type": "mobile" - } - }, - { - "desc": "Realme 3 Pro", - "ua": "Mozilla/5.0 (Linux; Android 11; RMX1851) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Realme", - "model": "RMX1851", - "type": "mobile" - } - }, - { - "desc": "Realme 8", - "ua": "Mozilla/5.0 (Linux; Android 12; RMX3085) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Realme", - "model": "RMX3085", - "type": "mobile" - } - }, - { - "desc": "Realme 9 Pro", - "ua": "Mozilla/5.0 (Linux; Android 13; RMX3471) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Realme", - "model": "RMX3471", - "type": "mobile" - } - }, - { - "desc": "Realme GT Master", - "ua": "Mozilla/5.0 (Linux; Android 13; RMX3363) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Realme", - "model": "RMX3363", - "type": "mobile" - } - }, - { - "desc": "Panasonic T31", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Panasonic T31 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Mobile Safari/537.36 ", - "expect": { - "vendor": "Panasonic", - "model": "T31", - "type": "mobile" - } - }, - { - "desc": "Panasonic TX-32CSW514 SmartTV", - "ua": "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", - "expect": { - "vendor": "Panasonic", - "model": "VIERA 2015", - "type": "smarttv" - } - }, - { - "desc": "Panasonic TX-40FXW724 SmartTV", - "ua": "HbbTV/1.4.1 (+DRM;Panasonic;SmartTV2018mid;3.024;4301-0003 0002-0000;SmartTV2018;)", - "expect": { - "vendor": "Panasonic", - "model": "SmartTV2018mid", - "type": "smarttv" - } - }, - { - "desc": "Panasonic TX-43HXW904 SmartTV", - "ua": "HbbTV/1.5.1 (+DRM;Panasonic;SmartTV2020mid;3.326;4301-0003 0008-0000;com.panasonic.SmartTV2020mid;)", - "expect": { - "vendor": "Panasonic", - "model": "SmartTV2020mid", - "type": "smarttv" - } - }, - { - "desc": "Panasonic DMR-HST130 SAT receiver", - "ua": "HbbTV/1.1.1 (+PVR;Panasonic;DIGA WebKit M8658;3.420;;)", - "expect": { - "vendor": "Panasonic", - "model": "DIGA WebKit M8658", - "type": "smarttv" - } - }, - { - "desc": "Philips SmartTV", - "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", - "expect": { - "vendor": "Philips", - "model": "", - "type": "smarttv" - } - }, - { - "desc": "Philips 32PFL6606K/02 SmartTV (2011)", - "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", - "expect": { - "vendor": "Philips", - "model": "", - "type": "smarttv" - } - }, - { - "desc": "Philips 32PFL6606K/02 SmartTV (2013)", - "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", - "expect": { - "vendor": "Philips", - "model": "", - "type": "smarttv" - } - }, - { - "desc": "Philips 32PHS5301/12 SmartTV (2016)", - "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36 OPR/29.0.1803.0 OMI/4.5.23.37.MOT2.13 HbbTV/1.2.1 (;Philips;32PHS5301/12;;_TV_MT5800;) Firmware/TPM161E_012.002.045.001 en", - "expect": { - "vendor": "Philips", - "model": "32PHS5301/12", - "type": "smarttv" - } - }, - { - "desc": "Pico 4", - "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", - "expect": { - "vendor": "PICO", - "model": "4", - "type": "xr" - } - }, - { - "desc": "Pico 4", - "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", - "expect": { - "vendor": "PICO", - "model": "4", - "type": "xr" - } - }, - { - "desc": "Pico Neo3 Link", - "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", - "expect": { - "vendor": "Pico", - "model": "Neo3 Link", - "type": "xr" - } - }, - { - "desc": "Polytron Prime 7 Pro", - "ua": "Mozilla/5.0 (Linux; U; Android 7.0; POLYTRON_P552 Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/64.0.3282.137 Mobile Safari/537.36 OPR/50.0.2254.149182", - "expect": { - "vendor": "POLYTRON", - "model": "P552", - "type": "mobile" - } - }, - { - "desc": "Polytron Rocket T1", - "ua": "Mozilla/5.0 (Linux; U; Android 5.0; en-US; POLYTRON R2501 Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/13.1.2.1293 Mobile Safari/537.36", - "expect": { - "vendor": "POLYTRON", - "model": "R2501", - "type": "mobile" - } - }, - { - "desc": "Polytron Rocket T6", - "ua": "Mozilla/5.0 (Linux; Android 7.0; POLYTRON R2509) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.92 Mobile Safari/537.36", - "expect": { - "vendor": "POLYTRON", - "model": "R2509", - "type": "mobile" - } - }, - { - "desc": "Polytron Zap 6 Posh", - "ua": "Mozilla/5.0 (Linux; U; Android 5.1; in-ID; POLYTRON_4G501 Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.0.1163 UCTurbo/1.9.9.900 Mobile Safari/537.36", - "expect": { - "vendor": "POLYTRON", - "model": "4G501", - "type": "mobile" - } - }, - { - "desc": "Roku", - "ua": "Mozilla/5.0 (Roku) AppleWebKit/537.36 (KHTML, like Gecko) Web/1.1 Safari/537.36", - "expect": { - "vendor": "Roku", - "model": "", - "type": "smarttv" - } - }, - { - "desc": "Roku", - "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 Roku/DVP-8.10 (468.10E04145A)", - "expect": { - "vendor": "Roku", - "model": "DVP-8.10", - "type": "smarttv" - } - }, - { - "desc": "Roku", - "ua": "Roku4640X/DVP-7.70 (297.70E04154A)", - "expect": { - "vendor": "Roku", - "model": "DVP-7.70", - "type": "smarttv" - } - }, - { - "desc": "Xiaomi TV", - "ua": "Mozilla/5.0 (Linux; Android 10; MiTV-MOOQ0 Build/QTG3.200305.006; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/94.0.4606.61 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MiTV-MOOQ0", - "type": "smarttv" - } - }, - { - "desc": "Kindle Fire HD", - "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.4 Mobile Safari/535.19 Silk-Accelerated=true", - "expect": { - "vendor": "Amazon", - "model": "KFTT", - "type": "tablet" - } - }, - { - "desc": "Kindle Fire HD", - "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.4 Mobile Safari/535.19 Silk-Accelerated=true", - "expect": { - "vendor": "Amazon", - "model": "KFTT", - "type": "tablet" - } - }, - { - "desc": "Echo Show 5", - "ua": "Mozilla/5.0 (Linux; Android 5.1; AEORK Build/LVY48F; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "AEORK", - "type": "tablet" - } - }, - { - "desc": "Echo Show 8", - "ua": "Mozilla/5.0 (Linux; Android 7.1; AEOCH) AppleWebKit/537.36 (KHTML, like Gecko) Silk/77.2.21 like Chrome/77.0.3865.92 Mobile Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "AEOCH", - "type": "tablet" - } - }, - { - "desc": "Echo Show 8", - "ua": "Mozilla/5.0 (Linux; Android 7.1.2; AEOCW) AppleWebKit/537.36 (KHTML, like Gecko) Silk/106.3.3 like Chrome/106.0.5249.170 Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "AEOCW", - "type": "tablet" - } - }, - { - "desc": "Echo Show 15", - "ua": "Mozilla/5.0 (Linux; Android 9; AEOHY) AppleWebKit/537.36 (KHTML, like Gecko) Silk/112.6.3 like Chrome/112.0.5615.213 Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "AEOHY", - "type": "tablet" - } - }, - { - "desc": "Echo Dot", - "ua": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEOBC Build/LVY48F)", - "expect": { - "vendor": "Amazon", - "model": "AEOBC", - "type": "embedded" - } - }, - { - "desc": "Samsung Galaxy A21s", - "ua": "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-A217F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.0 Chrome/75.0.3770.143 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-A217F", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy A31", - "ua": "Mozilla/5.0 (Linux; Android 10; SM-A315G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-A315G", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy A50", - "ua": "Mozilla/5.0 (Linux; Android 9; SM-A505F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.105 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-A505F", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy A50s", - "ua": "Mozilla/5.0 (Linux; Android 11; SM-A507FN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-A507FN", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy A52s", - "ua": "Mozilla/5.0 (Linux; Android 13; SM-A528B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-A528B", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy A80", - "ua": "Mozilla/5.0 (Linux; Android 9; SM-A805F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.112 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-A805F", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy Fold", - "ua": "Mozilla/5.0 (Linux; Android 9; SAMSUNG SM-F900U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/9.2 Chrome/67.0.3396.87 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-F900U", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy Z Flip", - "ua": "Mozilla/5.0 (Linux; Android 10; SM-F700N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-F700N", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy Z Fold2", - "ua": "Mozilla/5.0 (Linux; Android 10; SM-F916B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-F916B", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy S10E", - "ua": "Mozilla/5.0 (Linux; Android 9; SM-G970F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-G970F", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy S20 5G", - "ua": "Mozilla/5.0 (Linux; Android 10; SCG01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SCG01", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy Note 10+", - "ua": "Mozilla/5.0 (Linux; Android 9; SM-N976V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-N976V", - "type": "mobile" - } - }, - { - "desc": "Samsung SM-C5000", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; SM-C5000 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/51.0.2704.81 Mobile Safari/537.36 wkbrowser 4.1.35 3065", - "expect": { - "vendor": "Samsung", - "model": "SM-C5000", - "type": "mobile" - } - }, - { - "desc": "Samsung C8", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; SM-C7108) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-C7108", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy Note 8", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; GT-N5100 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "GT-N5100", - "type": "tablet" - } - }, - { - "desc": "Samsung SM-T231", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T231 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-T231", - "type": "tablet" - } - }, - { - "desc": "Samsung Galaxy Tab 6 Lite", - "ua": "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-P610) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/12.0 Chrome/79.0.3945.136 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-P610", - "type": "tablet" - } - }, - { - "desc": "Samsung Galaxy Tab A 9.7", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; SM-P550 Build/NMF26X; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.90 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-P550", - "type": "tablet" - } - }, - { - "desc": "Samsung Galaxy Tab A 10.1", - "ua": " Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-T515) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/13.0 Chrome/83.0.4103.106 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-T515", - "type": "tablet" - } - }, - { - "desc": "Samsung Galaxy Tab S7", - "ua": "Mozilla/5.0 (Linux; Android 10; SM-T870) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-T870", - "type": "tablet" - } - }, - { - "desc": "Samsung Galaxy Tab S8", - "ua": "Mozilla/5.0 (Linux; Android 12; SM-X706B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-X706B", - "type": "tablet" - } - }, - { - "desc": "Samsung Galaxy Tab S", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T700 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-T700", - "type": "tablet" - } - }, - { - "desc": "Samsung Galaxy Tab Pro 10.1", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T520 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-T520", - "type": "tablet" - } - }, - { - "desc": "Samsung Galaxy Watch", - "ua": "Mozilla/5.0 (Linux; Tizen 5.5; SAMSUNG SM-R805W) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.0 Chrome/69.0.3497.106 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-R805W", - "type": "wearable" - } - }, - { - "desc": "Samsung Galaxy Watch Active 2", - "ua": "Mozilla/5.0 (Linux; Tizen 5.5; SAMSUNG SM-R820) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.0 Chrome/69.0.3497.106 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-R820", - "type": "wearable" - } - }, - { - "desc": "Samsung Galaxy Watch4", - "ua": "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-R875U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.2. Chrome/102.0.5005.125 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-R875U", - "type": "wearable" - } - }, - { - "desc": "Samsung Galaxy Watch5 Pro", - "ua": "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-R925U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.2. Chrome/111.0.5563.116 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-R925U", - "type": "wearable" - } - }, - { - "desc": "Samsung Note 10.1", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-P605) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-P605", - "type": "tablet" - } - }, - { - "desc": "Samsung SmartTV2011", - "ua": "HbbTV/1.1.1 (;;;;;) Maple;2011", - "expect": { - "vendor": "Samsung", - "model": "SmartTV2011", - "type": "smarttv" - } - }, - { - "desc": "Samsung SmartTV2012", - "ua": "HbbTV/1.1.1 (;Samsung;SmartTV2012;;;) WebKit", - "expect": { - "vendor": "Samsung", - "model": "SmartTV2012", - "type": "smarttv" - } - }, - { - "desc": "Samsung SmartTV2014", - "ua": "HbbTV/1.1.1 (;Samsung;SmartTV2014;T-NT14UDEUC-1060.4;;) WebKit", - "expect": { - "vendor": "Samsung", - "model": "SmartTV2014", - "type": "smarttv" - } - }, - { - "desc": "Samsung SmartTV", - "ua": "Mozilla/5.0 (SMART-TV; X11; Linux armv7l) AppleWebkit/537.42 (KHTML, like Gecko) Safari/537.42", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "smarttv" - } - }, - { - "desc": "Samsung SmartTV", - "ua": "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", - "expect": { - "vendor": "Samsung", - "model": "undefined", - "type": "smarttv" - } - }, - { - "desc": "Samsung SmartTV HBBTV", - "ua": "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", - "expect": { - "vendor": "Samsung", - "model": "SmartTV2021:UAU7000", - "type": "smarttv" - } - }, - { - "desc": "Sharp AQUOS-TVX19B", - "ua": "Mozilla/5.0 (Linux; Android 9; AQUOS-TVX19B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Sharp", - "model": "AQUOS-TVX19B", - "type": "smarttv" - } - }, - { - "desc": "Sharp Aquos B10", - "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-A01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Mobile Safari/537.36", - "expect": { - "vendor": "Sharp", - "model": "SH-A01", - "type": "mobile" - } - }, - { - "desc": "Sharp Aquos L2", - "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-L02 Build/S4045) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", - "expect": { - "vendor": "Sharp", - "model": "SH-L02", - "type": "mobile" - } - }, - { - "desc": "Sharp Aquos L2", - "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-L02) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Sharp", - "model": "SH-L02", - "type": "mobile" - } - }, - { - "desc": "Sharp Aquos R2", - "ua": "Mozilla/5.0 (Linux; Android 8.0; SHV42) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36", - "expect": { - "vendor": "Sharp", - "model": "SHV42", - "type": "mobile" - } - }, - { - "desc": "Smartfren Andromax L", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Andromax B26D2H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", - "expect": { - "vendor": "Smartfren", - "model": "Andromax B26D2H", - "type": "mobile" - } - }, - { - "desc": "Smartfren Andromax G2", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Smartfren Andromax AD9A1H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.83 Mobile Safari/537.36", - "expect": { - "vendor": "Smartfren", - "model": "Andromax AD9A1H", - "type": "mobile" - } - }, - { - "desc": "Smartfren New Andromax I", - "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; id-id; New Andromax-i Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "Smartfren", - "model": "New Andromax-i", - "type": "mobile" - } - }, - { - "desc": "SONY Xperia 1 III", - "ua": "Mozilla/5.0 (Linux; Android 11; A101SO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "A101SO", - "type": "mobile" - } - }, - { - "desc": "Sony G8141 (Xperia XZ1)", - "ua": "Mozilla/5.0 (Linux; Android 9; SO-01K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "SO-01K", - "type": "mobile" - } - }, - { - "desc": "Sony G8141 (Xperia XZ Premium)", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; G8141) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "G8141", - "type": "mobile" - } - }, - { - "desc": "Sony C5303 (Xperia SP)", - "ua": "Mozilla/5.0 (Linux; Android 4.3; C5303 Build/12.1.A.1.205) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.93 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "C5303", - "type": "mobile" - } - }, - { - "desc": "Sony SO-02F (Xperia Z1 F)", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; SO-02F Build/14.1.H.2.119) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "SO-02F", - "type": "mobile" - } - }, - { - "desc": "Sony D6653 (Xperia Z3)", - "ua": "Mozilla/5.0 (Linux; Android 4.4; D6653 Build/23.0.A.0.376) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "D6653", - "type": "mobile" - } - }, - { - "desc": "Sony Xperia SOL25 (ZL2)", - "ua": "Mozilla/5.0 (Linux; U; Android 4.4; SOL25 Build/17.1.1.C.1.64) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "Sony", - "model": "SOL25", - "type": "mobile" - } - }, - { - "desc": "Sony Xperia SP", - "ua": "Mozilla/5.0 (Linux; Android 4.3; C5302 Build/12.1.A.1.201) AppleWebkit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "C5302", - "type": "mobile" - } - }, - { - "desc": "Sony Xperia L4", - "ua": "Mozilla/5.0 (Linux; Android 9; XQ-AD51) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.83 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "XQ-AD51", - "type": "mobile" - } - }, - { - "desc": "Sony Xperia 1ii", - "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AT51) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "XQ-AT51", - "type": "mobile" - } - }, - { - "desc": "Sony Xperia 1ii", - "ua": "Mozilla/5.0 (Linux; Android 10; SOG01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "SOG01", - "type": "mobile" - } - }, - { - "desc": "Sony Xperia 10ii", - "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AU52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "XQ-AU52", - "type": "mobile" - } - }, - { - "desc": "Sony Xperia Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AQ52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.185 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "XQ-AQ52", - "type": "mobile" - } - }, - { - "desc": "Sony SGP521 (Xperia Z2 Tablet)", - "ua": "Mozilla/5.0 (Linux; Android 4.4; SGP521 Build/17.1.A.0.432) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "Xperia Tablet", - "type": "tablet" - } - }, - { - "desc": "Sony Xperia Z2 Tablet", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SGP561) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "Xperia Tablet", - "type": "tablet" - } - }, - { - "desc": "Sony Tablet S", - "ua": "Mozilla/5.0 (Linux; U; Android 3.1; Sony Tablet S Build/THMAS10000) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13", - "expect": { - "vendor": "Sony", - "model": "Xperia Tablet", - "type": "tablet" - } - }, - { - "desc": "Sony Tablet Z LTE", - "ua": "Mozilla/5.0 (Linux; U; Android 4.1; SonySGP321 Build/10.2.C.0.143) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", - "expect": { - "vendor": "Sony", - "model": "Xperia Tablet", - "type": "tablet" - } - }, - { - "desc": "Sony BRAVIA 4K GB ATV3", - "ua": "Mozilla/5.0 (Linux; Andr0id 9; BRAVIA 4K GB ATV3 Build/PTT1.190515.001.S38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 OPR/46.0.2207.0 OMI/4.13.0.180.DIA5.104 Model/Sony-BRAVIA-4K-GB-ATV3", - "expect": { - "vendor": "Sony", - "model": "BRAVIA 4K GB ATV3", - "type": "smarttv" - } - }, - { - "desc": "Sony BRAVIA 4K GB ATV3", - "ua": "Mozilla/5.0 (Linux; Android 9; BRAVIA 4K GB ATV3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Sony", - "model": "BRAVIA 4K GB ATV3", - "type": "smarttv" - } - }, - { - "desc": "Sony Bravia 4k UR2", - "ua": "Mozilla/5.0 (Linux: Andr0id 9: BRAVIA 4K UR2 Build/PTT1.190515.001.S104) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 OPR/46.0.2207.0 OMI/4.13.5.431.DIA5HBBTV.250 Model/Sony-BRAVIA-4K-UR2", - "expect": { - "vendor": "Sony", - "model": "BRAVIA 4K UR2", - "type": "smarttv" - } - }, - { - "desc": "TCL 10 5G", - "ua": "Mozilla/5.0 (Linux; Android 11; T790Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 EdgA/114.0.1823.43", - "expect": { - "vendor": "TCL", - "model": "T790Y", - "type": "mobile" - } - }, - { - "desc": "TCL 10 5G UW", - "ua": "Mozilla/5.0 (Linux; Android 10; T790S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T790S", - "type": "mobile" - } - }, - { - "desc": "TCL 10 Plus", - "ua": "Mozilla/5.0 (Linux; Android 11; T782H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36 OPR/64.3.3282.60839", - "expect": { - "vendor": "TCL", - "model": "T782H", - "type": "mobile" - } - }, - { - "desc": "TCL 10 Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; T799B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T799B", - "type": "mobile" - } - }, - { - "desc": "TCL 10 SE", - "ua": "Mozilla/5.0 (Linux; Android 10; T766H_RU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T766H", - "type": "mobile" - } - }, - { - "desc": "TCL 10 TabMax", - "ua": "Mozilla/5.0 (Linux; Android 11; 9296Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "9296Q", - "type": "tablet" - } - }, - { - "desc": "TCL 10 TabMax 4G", - "ua": "Mozilla/5.0 (Linux; Android 10; 9295G_EEA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "9295G", - "type": "tablet" - } - }, - { - "desc": "TCL 10 TabMax WiFi", - "ua": "Mozilla/5.0 (Linux; Android 10; 9296G_TR) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "9296G", - "type": "tablet" - } - }, - { - "desc": "TCL 10L", - "ua": "Mozilla/5.0 (Linux; Android 10; T770B Build/QKQ1.200329.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 GSA/11.41.10.23.arm64", - "expect": { - "vendor": "TCL", - "model": "T770B", - "type": "mobile" - } - }, - { - "desc": "TCL 10L", - "ua": "Mozilla/5.0 (Linux; Android 11; T770H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T770H", - "type": "mobile" - } - }, - { - "desc": "TCL 20 5G", - "ua": "Mozilla/5.0 (Linux; Android 11; T781) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T781", - "type": "mobile" - } - }, - { - "desc": "TCL 20 Pro 5G", - "ua": "Mozilla/5.0 (Linux; Android 12; T810S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36 EdgA/113.0.1774.63", - "expect": { - "vendor": "TCL", - "model": "T810S", - "type": "mobile" - } - }, - { - "desc": "TCL 20 SE", - "ua": "Mozilla/5.0 (Linux; Android 11; T671H Build/RKQ1.201112.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.73 Mobile Safari/537.36 GoogleApp/13.9.7.23.arm64", - "expect": { - "vendor": "TCL", - "model": "T671H", - "type": "mobile" - } - }, - { - "desc": "TCL 20 XE", - "ua": "Mozilla/5.0 (Linux; Android 11; 5087Z) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "5087Z", - "type": "mobile" - } - }, - { - "desc": "TCL 20B", - "ua": "Mozilla/5.0 (Linux; Android 11; 6159K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "6159K", - "type": "mobile" - } - }, - { - "desc": "TCL 205", - "ua": "Mozilla/5.0 (Linux; Android 11; 4187D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "4187D", - "type": "mobile" - } - }, - { - "desc": "TCL 20E", - "ua": "Mozilla/5.0 (Linux; Android 11; 6125A) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/18.0 Chrome/99.0.4844.88 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "6125A", - "type": "mobile" - } - }, - { - "desc": "TCL 20L", - "ua": "Mozilla/5.0 (Linux; Android 11; T774H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.59 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T774H", - "type": "mobile" - } - }, - { - "desc": "TCL 20L Plus", - "ua": "Mozilla/5.0 (Linux; Android 11; T775H Build/RKQ1.210107.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.61 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T775H", - "type": "mobile" - } - }, - { - "desc": "TCL 20R 5G", - "ua": "Mozilla/5.0 (Linux; Android 11; T767H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36 OPR/71.3.3718.67322", - "expect": { - "vendor": "TCL", - "model": "T767H", - "type": "mobile" - } - }, - { - "desc": "TCL 20S", - "ua": "Mozilla/5.0 (Linux; Android 11; T773O) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T773O", - "type": "mobile" - } - }, - { - "desc": "TCL 20Y", - "ua": "Mozilla/5.0 (Linux; Android 11; 6156D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.134 Mobile Safari/537.36 OPR/70.3.3653.66287", - "expect": { - "vendor": "TCL", - "model": "6156D", - "type": "mobile" - } - }, - { - "desc": "TCL 30 V 5G", - "ua": "Mozilla/5.0 (Linux; Android 11; T781S Build/RKQ1.210614.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/369.0.0.5.110;]", - "expect": { - "vendor": "TCL", - "model": "T781S", - "type": "mobile" - } - }, - { - "desc": "TCL 30 XE 5G", - "ua": "Mozilla/5.0 (Linux; Android 12; T767W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/416.0.0.35.85;]", - "expect": { - "vendor": "TCL", - "model": "T767W", - "type": "mobile" - } - }, - { - "desc": "TCL 305", - "ua": "Mozilla/5.0 (Linux; arm; Android 11; 6102D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.167 YaBrowser/22.7.6.96.00 SA/3 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "6102D", - "type": "mobile" - } - }, - { - "desc": "TCL 306", - "ua": "Mozilla/5.0 (Linux; Android 12; 6102H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.141 Mobile Safari/537.36[FBAN/EMA;FBLC/it_IT;FBAV/332.0.0.22.108;]", - "expect": { - "vendor": "TCL", - "model": "6102H", - "type": "mobile" - } - }, - { - "desc": "TCL 30", - "ua": "Mozilla/5.0 (Linux; Android 12; T676H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T676H", - "type": "mobile" - } - }, - { - "desc": "TCL 30+", - "ua": "Mozilla/5.0 (Linux; Android 12; T676J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T676J", - "type": "mobile" - } - }, - { - "desc": "TCL 30 5G", - "ua": "Mozilla/5.0 (Linux; Android 12; T776H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T776H", - "type": "mobile" - } - }, - { - "desc": "TCL 30 LE", - "ua": "Mozilla/5.0 (Linux; Android 12; 4188V Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/352.0.0.14.108;]", - "expect": { - "vendor": "TCL", - "model": "4188V", - "type": "mobile" - } - }, - { - "desc": "TCL 30 SE", - "ua": "Mozilla/5.0 (Linux; Android 12; 6165H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/396.1.0.28.104;]", - "expect": { - "vendor": "TCL", - "model": "6165H", - "type": "mobile" - } - }, - { - "desc": "TCL 30E", - "ua": "Mozilla/5.0 (Linux; Android 12; 6127I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "6127I", - "type": "mobile" - } - }, - { - "desc": "TCL 40 NxtPaper", - "ua": "Mozilla/5.0 (Linux; Android 13; T612B Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.53 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T612B", - "type": "mobile" - } - }, - { - "desc": "TCL A3", - "ua": "Mozilla/5.0 (Linux; Android 11; A509DL Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 GSA/13.18.7.23.arm64", - "expect": { - "vendor": "TCL", - "model": "A509DL", - "type": "mobile" - } - }, - { - "desc": "TCL A30", - "ua": "Mozilla/5.0 (Linux; Android 11; 5102L Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/413.0.0.30.104;]", - "expect": { - "vendor": "TCL", - "model": "5102L", - "type": "mobile" - } - }, - { - "desc": "TCL 40 SE", - "ua": "Mozilla/5.0 (Linux; Android 13; T610K Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T610K", - "type": "mobile" - } - }, - { - "desc": "TCL 40 XE 5G", - "ua": "Mozilla/5.0 (Linux; Android 13; T609DL Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.136 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/466.1.0.57.85;]", - "expect": { - "vendor": "TCL", - "model": "T609DL", - "type": "mobile" - } - }, - { - "desc": "TCL 403", - "ua": "Mozilla/5.0 (Linux; Android 12; T431D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T431D", - "type": "mobile" - } - }, - { - "desc": "TCL 405", - "ua": "Mozilla/5.0 (Linux; Android 12; T506D Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/418.0.0.33.69;]", - "expect": { - "vendor": "TCL", - "model": "T506D", - "type": "mobile" - } - }, - { - "desc": "TCL 408", - "ua": "Mozilla/5.0 (Linux; U; Android 12; T507U Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/105.0.5195.136 Mobile Safari/537.36 OPR/75.0.2254.68857", - "expect": { - "vendor": "TCL", - "model": "T507U", - "type": "mobile" - } - }, - { - "desc": "TCL 40R 5G", - "ua": "Mozilla/5.0 (Linux; Android 12; T771K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 EdgA/114.0.1823.37", - "expect": { - "vendor": "TCL", - "model": "T771K", - "type": "mobile" - } - }, - { - "desc": "TCL Ion X", - "ua": "Mozilla/5.0 (Linux; Android 12; T430W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.60 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T430W", - "type": "mobile" - } - }, - { - "desc": "TCL NxtPaper 11", - "ua": "Mozilla/5.0 (Linux; Android 13; 9466X Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.179 Safari/537.36 [FB_IAB/FB4A;FBAV/473.0.0.41.81;]", - "expect": { - "vendor": "TCL", - "model": "9466X", - "type": "tablet" - } - }, - { - "desc": "TCL Stylus 5G", - "ua": "Mozilla/5.0 (Linux; Android 12; T779W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.2 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "T779W", - "type": "mobile" - } - }, - { - "desc": "TCL Tab 8 4G", - "ua": "Mozilla/5.0 (Linux; Android 10; 9048S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "9048S", - "type": "tablet" - } - }, - { - "desc": "TCL Tab 8 LE", - "ua": "Mozilla/5.0 (Linux; Android 12; 9137W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.61 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "9137W", - "type": "tablet" - } - }, - { - "desc": "TCL Tab 10 FHD 4G", - "ua": "Mozilla/5.0 (Linux; Android 11; 9060G Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "9060G", - "type": "tablet" - } - }, - { - "desc": "TCL Tab 10 HD 4G", - "ua": "Mozilla/5.0 (Linux; Android 11; 9060X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "9060X", - "type": "tablet" - } - }, - { - "desc": "TCL Tab 10 LTE", - "ua": "Mozilla/5.0 (Linux; Android 13; 8196G Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.162 Safari/537.36 [FB_IAB/FB4A;FBAV/471.0.0.35.80;]", - "expect": { - "vendor": "TCL", - "model": "8196G", - "type": "tablet" - } - }, - { - "desc": "TCL Tab 10 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 13; 8496G Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.61 Safari/537.36 [FB_IAB/FB4A;FBAV/474.0.0.52.74;]", - "expect": { - "vendor": "TCL", - "model": "8496G", - "type": "tablet" - } - }, - { - "desc": "TCL Tab 10L", - "ua": "Mozilla/5.0 (Linux; Android 11; 8491X_EEA Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.105 Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "8491X", - "type": "tablet" - } - }, - { - "desc": "TCL Tab 10s 4G", - "ua": "Mozilla/5.0 (Linux; Android 11; 9080G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "9080G", - "type": "tablet" - } - }, - { - "desc": "TCL Xess P17AA", - "ua": "Mozilla/5.0 (Linux; Android 5.1; TCL Xess P17AA Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Safari/537.36", - "expect": { - "vendor": "TCL", - "model": "Xess P17AA", - "type": "tablet" - } - }, - { - "desc": "Tecno KC8", - "ua": "Mozilla/5.0 (Linux; Android 10; TECNO KC8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TECNO", - "model": "KC8", - "type": "mobile" - } - }, - { - "desc": "Tecno Spark 8C", - "ua": "Mozilla/5.0 (Linux; Android 11; TECNO KG5n) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "TECNO", - "model": "KG5n", - "type": "mobile" - } - }, - { - "desc": "Tesla", - "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1", - "expect": { - "vendor": "Tesla", - "model": "undefined", - "type": "embedded" - } - }, - { - "desc": "Tesla", - "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.16.2.1-e99c70fff409", - "expect": { - "vendor": "Tesla", - "model": "undefined", - "type": "embedded" - } - }, - { - "desc": "TechniSAT Digit ISIO S SAT receiver", - "ua": "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50", - "expect": { - "vendor": "TechniSat", - "model": "Digit ISIO S", - "type": "smarttv" - } - }, - { - "desc": "TechniSAT MultyVision SmartTV", - "ua": "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50", - "expect": { - "vendor": "TechniSat", - "model": "MultyVision ISIO", - "type": "smarttv" - } - }, - { - "desc": "Ulefone Armor", - "ua": "Mozilla/5.0 (Linux; Android 6.0; Armor Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36", - "expect": { - "vendor": "Ulefone", - "model": "Armor", - "type": "mobile" - } - }, - { - "desc": "Ulefone Armor", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 6.0; Armor) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 YaBrowser/20.4.2.101.00 SA/1 Mobile Safari/537.36", - "expect": { - "vendor": "Ulefone", - "model": "Armor", - "type": "mobile" - } - }, - { - "desc": "Ulefone Armor 8 Pro", - "ua": "Mozilla/5.0 (Linux; Android 11; Armor 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.192 Mobile Safari/537.36 OPR/74.1.3922.71199", - "expect": { - "vendor": "Ulefone", - "model": "Armor 8 Pro", - "type": "mobile" - } - }, - { - "desc": "Ulefone Armor 12 5G", - "ua": "Mozilla/5.0 (Linux; Android 11; Armor 12 5G Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36", - "expect": { - "vendor": "Ulefone", - "model": "Armor 12 5G", - "type": "mobile" - } - }, - { - "desc": "Ulefone Armor 20WT", - "ua": "Mozilla/5.0 (Linux; Android 12; Armor 20WT) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Mobile Safari/537.36", - "expect": { - "vendor": "Ulefone", - "model": "Armor 20WT", - "type": "mobile" - } - }, - { - "desc": "Ulefone Armor Pad", - "ua": "Mozilla/5.0 (Linux; Android 12; Armor Pad Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/431.0.0.30.108;]", - "expect": { - "vendor": "Ulefone", - "model": "Armor Pad", - "type": "mobile" - } - }, - { - "desc": "Ulefone Armor X5 Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; Armor X5 Pro Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/430.0.0.23.113;]", - "expect": { - "vendor": "Ulefone", - "model": "Armor X5 Pro", - "type": "mobile" - } - }, - { - "desc": "Ulefone Power Armor 14 Pro", - "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor14 Pro Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.138 Mobile Safari/537.36", - "expect": { - "vendor": "Ulefone", - "model": "Power Armor14 Pro", - "type": "mobile" - } - }, - { - "desc": "Ulefone Power Armor 18T", - "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor 18T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Ulefone", - "model": "Power Armor 18T", - "type": "mobile" - } - }, - { - "desc": "Ulefone Power Armor 19T", - "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor 19T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.192 Mobile Safari/537.36 OPR/74.3.3922.71982", - "expect": { - "vendor": "Ulefone", - "model": "Power Armor 19T", - "type": "mobile" - } - }, - { - "desc": "Xiaomi 2201117TG", - "ua": "Mozilla/5.0 (Linux; Android 11; 2201117TG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.98 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "2201117TG", - "type": "mobile" - } - }, - { - "desc": "Xiaomi M2004J19C", - "ua": "Mozilla/5.0 (Linux; Android 11; M2004J19C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.77 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2004J19C", - "type": "mobile" - } - }, - { - "desc": "Xiaomi M2006C3MNG", - "ua": "Mozilla/5.0 (Linux; Android 11; M2006C3MNG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2006C3MNG", - "type": "mobile" - } - }, - { - "desc": "Xiaomi 21061119DG", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; 21061119DG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 YaBrowser/23.3.7.24.00 SA/3 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "21061119DG", - "type": "mobile" - } - }, - { - "desc": "Xiaomi 2013023", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; en-US; 2013023 Build/HM2013023) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/10.0.1.512 U3/0.8.0 Mobile Safari/533.1", - "expect": { - "vendor": "Xiaomi", - "model": "2013023", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Hongmi Note 1W", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-CN; HM NOTE 1W Build/JDQ39) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.7.9.439 U3/0.8.0 Mobile Safari/533.1", - "expect": { - "vendor": "Xiaomi", - "model": "HM NOTE 1W", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi 3C", - "ua": "Mozilla/5.0 (Linux; U; Android 4.3; zh-CN; MI 3C Build/JLS36C) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.7.9.439 U3/0.8.0 Mobile Safari/533.1", - "expect": { - "vendor": "Xiaomi", - "model": "MI 3C", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi 5", - "ua": "Mozilla/5.0 (Linux; Android 7.0; MI 5 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MI 5", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi 6", - "ua": "Mozilla/5.0 (Linux; Android 7.1; MI 6 Build/NMF26X; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/59.0.3071.125 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MI 6", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi 10 Pro", - "ua": "Linux; U; Android 13; Mi 10 Pro Build/TKQ1.221114.001", - "expect": { - "vendor": "Xiaomi", - "model": "Mi 10 Pro", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi 5s Plus", - "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; MI 5s Plus Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/8.7.1", - "expect": { - "vendor": "Xiaomi", - "model": "MI 5s Plus", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi A1", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; Mi A1 Build/OPR1.170623.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Mi A1", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi Note", - "ua": "Mozilla/5.0 (Linux; Android 4.4.4; MI NOTE LTE Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MI NOTE LTE", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi One Plus", - "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; MI-ONE Plus Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "expect": { - "vendor": "Xiaomi", - "model": "MI-ONE Plus", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi Max 3", - "ua": "Mozilla/5.0 (Linux; Android 9; MI MAX 3 Build/PKQ1.181007.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MI MAX 3", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi A1", - "ua": "Mozilla/5.0 (Linux; Android 9; Mi A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.101 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Mi A1", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi A2 Lite", - "ua": "Mozilla/5.0 (Linux; Android 9; Mi A2 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Mi A2 Lite", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi 9 SE", - "ua": "Mozilla/5.0 (Linux; Android 9; Mi 9 SE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Mi 9 SE", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi A2", - "ua": "Mozilla/5.0 (Linux; Android 9; Mi A2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Mi A2", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Mi CC9", - "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-cn; MI CC 9 Build/RKQ1.200826.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.116 Mobile Safari/537.36 XiaoMi/MiuiBrowser/15.5.18", - "expect": { - "vendor": "Xiaomi", - "model": "MI CC 9", - "type": "mobile" - } - }, - { - "desc": "Xiaomi MI PAD", - "ua": "Mozilla/5.0 (Linux; U; Android 4.4.4; en-us; MI PAD Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/9.3.2", - "expect": { - "vendor": "Xiaomi", - "model": "MI PAD", - "type": "tablet" - } - }, - { - "desc": "Xiaomi MI PAD 2", - "ua": "Mozilla/5.0 (Linux; Android 5.1; MI PAD 2 Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Safari/537.36 [FB_IAB/FB4A;FBAV/137.0.0.24.91;]", - "expect": { - "vendor": "Xiaomi", - "model": "MI PAD 2", - "type": "tablet" - } - }, - { - "desc": "Xiaomi MI PAD 2", - "ua": "Mozilla/5.0 (Linux; x86_64; Android 5.1; MI PAD 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 YaBrowser/20.11.2.69.01 Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MI PAD 2", - "type": "tablet" - } - }, - { - "desc": "Xiaomi MI PAD 3", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.0; MI PAD 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.216 YaBrowser/21.5.6.56.01 Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MI PAD 3", - "type": "tablet" - } - }, - { - "desc": "Xiaomi MI PAD 4", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 8.1.0; MI PAD 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 YaBrowser/19.9.1.126.01 Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MI PAD 4", - "type": "tablet" - } - }, - { - "desc": "Xiaomi MI PAD 4 PLUS", - "ua": "Mozilla/5.0 (Linux; Android 8.1; MI PAD 4 PLUS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "MI PAD 4 PLUS", - "type": "tablet" - } - }, - { - "desc": "Xiaomi MI PAD 4 WiFi", - "ua": "Mozilla/5.0 (Linux; Android 8.1; Mi Pad4 Wi-Fi) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Mobile Safari/537.36 EdgA/86.0.622.61", - "expect": { - "vendor": "Xiaomi", - "model": "Mi Pad4 Wi-Fi", - "type": "tablet" - } - }, - { - "desc": "Xiaomi Mi Pad 5", - "ua": "Mozilla/5.0 (Linux; Android 13; 21051182G Build/TKQ1.221013.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", - "expect": { - "vendor": "Xiaomi", - "model": "21051182G", - "type": "tablet" - } - }, - { - "desc": "Xiaomi Mi Pad 5 Pro", - "ua": "Mozilla/5.0 (Linux; Android 11; M2105K81AC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Safari/537.36 Line/13.15.1/IAB", - "expect": { - "vendor": "Xiaomi", - "model": "M2105K81AC", - "type": "tablet" - } - }, - { - "desc": "Xiaomi Mi Pad 5 Pro 5G", - "ua": "Mozilla/5.0 (Linux; Android 12; M2105K81C) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2105K81C", - "type": "tablet" - } - }, - { - "desc": "Xiaomi Mi Pad 6 Max 14", - "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-tw; 2307BRPDCC Build/UKQ1.230804.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.118 Mobile Safari/537.36 Device/yudi Model/2307BRPDCC XiaoMi/MiuiBrowser/14.10.6", - "expect": { - "vendor": "Xiaomi", - "model": "2307BRPDCC", - "type": "tablet" - } - }, - { - "desc": "Xiaomi Mi Pad 6 Pro", - "ua": "Mozilla/5.0 (Linux; U; Android 13; en-US; 23046RP50C Build/TKQ1.221114.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.6.2.1316 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "23046RP50C", - "type": "tablet" - } - }, - { - "desc": "Xiaomi Pad 6S Pro 12.4", - "ua": "Mozilla/5.0 (Linux; Android 14; 24018RPACC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "24018RPACC", - "type": "tablet" - } - }, - { - "desc": "Xiaomi POCO X2", - "ua": "Mozilla/5.0 (Linux; Android 10; POCO X2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "POCO X2", - "type": "mobile" - } - }, - { - "desc": "Xiaomi POCO X3 Pro", - "ua": "Mozilla/5.0 (Linux; Android 11; M2102J20SI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2102J20SI", - "type": "mobile" - } - }, - { - "desc": "Xiaomi POCO X3 Pro", - "ua": "Mozilla/5.0 (Linux; Android 12; M2102J20SG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2102J20SG", - "type": "mobile" - } - }, - { - "desc": "Xiaomi POCO X3 NFC", - "ua": "Mozilla/5.0 (Linux; Android 12; M2007J20CG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2007J20CG", - "type": "mobile" - } - }, - { - "desc": "Xiaomi POCO M2 Pro", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; POCO M2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.7.42.00 SA/3 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "POCO M2 Pro", - "type": "mobile" - } - }, - { - "desc": "Xiaomi POCO M3", - "ua": "Mozilla/5.0 (Linux; Android 10; M2010J19CI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2010J19CI", - "type": "mobile" - } - }, - { - "desc": "Xiaomi POCOPHONE F1", - "ua": "Mozilla/5.0 (Linux; Android 10; POCOPHONE F1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "POCOPHONE F1", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Redmi 4A", - "ua": "Mozilla/5.0 (Linux; Android 6.0; Redmi 4A Build/MMB29M; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Redmi 4A", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Redmi 10C", - "ua": "Mozilla/5.0 (Linux; Android 12; 220333QAG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "220333QAG", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Redmi K30 5G", - "ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Redmi K30 5G", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Redmi K30 Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Redmi K30 Pro", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Redmi Note 3", - "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Redmi Note 3 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.116 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Redmi Note 3", - "type": "mobile" - } - }, - { - "desc": "Xiaomi Redmi Note 9 Pro Max", - "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 9 Pro Max) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.99 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Redmi Note 9 Pro Max", - "type": "mobile" - } - }, - { - "desc": "XiaoMi Redmi Note 9S", - "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 9S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Redmi Note 9S", - "type": "mobile" - } - }, - { - "desc": "XiaoMi Redmi Note 10 5G", - "ua": "Mozilla/5.0 (Linux; Android 12; M2103K19C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2103K19C", - "type": "mobile" - } - }, - { - "desc": "XiaoMi Redmi Note 10 Pro", - "ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2101K6P", - "type": "mobile" - } - }, - { - "desc": "XiaoMi Redmi Note 10 Pro", - "ua": "Mozilla/5.0 (Linux; Android 12; M2101K6G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "M2101K6G", - "type": "mobile" - } - }, - { - "desc": "XiaoMi Redmi Note 8", - "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Xiaomi", - "model": "Redmi Note 8", - "type": "mobile" - } - }, - { - "desc": "XiaoMi Redmi Note 12 Turbo", - "ua": "Mozilla/5.0 (Linux; Android 13; 23049RAD8C; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 VivoBrowser/16.7.1.1", - "expect": { - "vendor": "Xiaomi", - "model": "23049RAD8C", - "type": "mobile" - } - }, - { - "desc": "XiaoMi Redmi Pad", - "ua": "Mozilla/5.0 (Linux; U; Android 12; id-id; Redmi Pad Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36 XiaoMi/MiuiBrowser/14.1.1-gn", - "expect": { - "vendor": "Xiaomi", - "model": "Redmi Pad", - "type": "tablet" - } - }, - { - "desc": "XiaoMi Redmi Pad", - "ua": "Mozilla/5.0 (Linux; Android 14; 22081283G Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", - "expect": { - "vendor": "Xiaomi", - "model": "22081283G", - "type": "tablet" - } - }, - { - "desc": "XiaoMi Redmi Pad Pro", - "ua": "Mozilla/5.0 (Linux; Android 14; 2405CRPFDG Build/UKQ1.240116.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.97 Safari/537.36 [FB_IAB/FB4A;FBAV/476.0.0.49.74;] FBNV/1", - "expect": { - "vendor": "Xiaomi", - "model": "2405CRPFDG", - "type": "tablet" - } - }, - { - "desc": "XiaoMi Redmi Pad SE", - "ua": "Dalvik/2.1.0 (Linux; U; Android 14; 23073RPBFG Build/UKQ1.231003.002)", - "expect": { - "vendor": "Xiaomi", - "model": "23073RPBFG", - "type": "tablet" - } - }, - { - "desc": "XiaoMi Redmi Pad SE 8.7", - "ua": "Mozilla/5.0 (Linux; Android 14; 24076RP19G Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Mobile Safari/537.36 Line/14.18.1/IAB", - "expect": { - "vendor": "Xiaomi", - "model": "24076RP19G", - "type": "tablet" - } - }, - { - "desc": "ZTE Blade A6", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE BLADE A0620 Build/NMF26F; ru-ru) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36 Puffin/9.2.0.50586AP", - "expect": { - "vendor": "ZTE", - "model": "BLADE A0620", - "type": "mobile" - } - }, - { - "desc": "PlayStation 4", - "ua": "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", - "expect": { - "vendor": "Sony", - "model": "PlayStation 4", - "type": "console" - } - }, - { - "desc": "PlayStation 5", - "ua": "Mozilla/5.0 (Playstation; Playstation 5/1.05) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15", - "expect": { - "vendor": "Sony", - "model": "Playstation 5", - "type": "console" - } - }, - { - "desc": "PlayStation Vita", - "ua": "Mozilla/5.0 (PlayStation Vita 3.52) AppleWebKit/537.73 (KHTML, like Gecko) Silk/3.2", - "expect": { - "vendor": "Sony", - "model": "PlayStation Vita", - "type": "console" - } - }, - { - "desc": "Nintendo Switch", - "ua": "Mozilla/5.0 (Nintendo Switch; WifiWebAuthApplet) AppleWebKit/606.4 (KHTML, like Gecko) NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393", - "expect": { - "vendor": "Nintendo", - "model": "Switch", - "type": "console" - } - }, - { - "desc": "Nintendo WiiU", - "ua": "Mozilla/5.0 (Nintendo WiiU) AppleWebKit/536.30 (KHTML, like Gecko) NX/3.0.4.2.9 NintendoBrowser/4.2.0.11146.EU", - "expect": { - "vendor": "Nintendo", - "model": "WiiU", - "type": "console" - } - }, - { - "desc": "Nintendo Wii", - "ua": "Opera/9.10 (Nintendo Wii; U; ; 1621; en)", - "expect": { - "vendor": "Nintendo", - "model": "Wii", - "type": "console" - } - }, - { - "desc": "Nintendo 3DS", - "ua": "Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7610.EU", - "expect": { - "vendor": "Nintendo", - "model": "3DS", - "type": "console" - } - }, - { - "desc": "Nintendo 3DS", - "ua": "Mozilla/5.0 (New Nintendo 3DS like iPhone) AppleWebKit/536.30 (KHTML, like Gecko) NX/3.0.0.5.15 Mobile NintendoBrowser/1.3.10126.EU", - "expect": { - "vendor": "Nintendo", - "model": "3DS", - "type": "console" - } - }, - { - "desc": "Galaxy Nexus", - "ua": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19", - "expect": { - "vendor": "Samsung", - "model": "Galaxy Nexus", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy C9 Pro", - "ua": "Mozilla/5.0 (Linux; Android 6.0; SAMSUNG SM-C900F Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.2 Chrome/44.0.2403.133 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-C900F", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy S5", - "ua": "Mozilla/5.0 (Linux; Android 5.0; SM-G900F Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.78 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-G900F", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy J7 Prime", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; SM-G610F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-G610F", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy S6", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-G920I Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-G920I", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy S6 Edge", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-G925I Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-G925I", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy Note 5 Chrome", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-N920C Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-N920C", - "type": "mobile" - } - }, - { - "desc": "Samsung Galaxy Note 5 Samsung Browser", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SAMSUNG SM-N920C Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36", - "expect": { - "vendor": "Samsung", - "model": "SM-N920C", - "type": "mobile" - } - }, - { - "desc": "Google Chromecast with Google TV", - "ua": "Mozilla/5.0 (Linux; Android 12.0; Build/STTL.240206.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.0 Safari/537.36 CrKey/1.56.500000 DeviceType/AndroidTV", - "expect": { - "vendor": "Google", - "model": "Chromecast AndroidTV", - "type": "smarttv" - } - }, - { - "desc": "Google Chromecast Mini Smart Speaker", - "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/SmartSpeaker", - "expect": { - "vendor": "Google", - "model": "Chromecast SmartSpeaker", - "type": "smarttv" - } - }, - { - "desc": "Google Chromecast Third Generation", - "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/Chromecast", - "expect": { - "vendor": "Google", - "model": "Chromecast Third Generation", - "type": "smarttv" - } - }, - { - "desc": "Google Chromecast Nest Hub", - "ua": "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 CrKey/1.56.500000", - "expect": { - "vendor": "Google", - "model": "Chromecast Nest Hub", - "type": "smarttv" - } - }, - { - "desc": "Google Chromecast", - "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.84 Safari/537.36 CrKey/1.22.79313", - "expect": { - "vendor": "Google", - "model": "Chromecast", - "type": "smarttv" - } - }, - { - "desc": "Google Pixel C", - "ua": "Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel C", - "type": "tablet" - } - }, - { - "desc": "Google Pixel C", - "ua": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.64 Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel C", - "type": "tablet" - } - }, - { - "desc": "Google Pixel", - "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel Build/NDE63V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel", - "type": "mobile" - } - }, - { - "desc": "Google Pixel Tablet", - "ua": "Mozilla/5.0 (Linux; Android 14; Pixel Tablet Build/AP2A.240905.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel Tablet", - "type": "tablet" - } - }, - { - "desc": "Google Pixel Watch", - "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch Build/TWD4.231005.002)", - "expect": { - "vendor": "Google", - "model": "Pixel Watch", - "type": "wearable" - } - }, - { - "desc": "Google Pixel Watch 2", - "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch 2 Build/TWD9.240605.001.A1)", - "expect": { - "vendor": "Google", - "model": "Pixel Watch 2", - "type": "wearable" - } - }, - { - "desc": "Google Pixel XL", - "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel XL", - "ua": "Mozilla/5.0 (Linux; Android 9; Pixel XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 2", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 2", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 2 XL", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 XL Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 2 XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 2 XL", - "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 2 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 2 XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 3", - "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 3", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 3 XL", - "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 3 XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 3 XL", - "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 3 XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 3a", - "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 3a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 3a", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 3a XL", - "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 3a XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 3a XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 4", - "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 4", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 4a", - "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.83 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 4a", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 4 XL", - "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 4 XL", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 5", - "ua": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.120 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 5", - "type": "mobile" - } - }, - { - "desc": "Google Pixel 7", - "ua": "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel 7", - "type": "mobile" - } - }, - { - "desc": "Generic Android Device", - "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; i980 Build/MRA58K)", - "expect": { - "vendor": "Generic", - "model": "Android 6.0.1" - } - }, - { - "desc": "Android Phone Unidentified Vendor (docomo F-04K)", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; F-04K Build/V15R060P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", - "expect": { - "model": "F-04K", - "type": "mobile" - } - }, - { - "desc": "docomo SH-02M", - "ua": "Mozilla/5.0 (Linux; Android 9; SH-02M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", - "expect": { - "vendor": "Sharp", - "model": "SH-02M", - "type": "mobile" - } - }, - { - "desc": "Android Tablet Unidentified Vendor (docomo F-02K)", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; F-02K Build/V44R059G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36", - "expect": { - "model": "F-02K", - "type": "tablet" - } - }, - { - "desc": "Android Tablet Unidentified Vendor (docomo d-02K)", - "ua": "Mozilla/5.0 (Linux; Android 9; d-02K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Safari/537.36", - "expect": { - "model": "d-02K", - "type": "tablet" - } - }, - { - "desc": "LG VK Series Tablet", - "ua": "Mozilla/5.0 (Linux; Android 5.0.2; VK700 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.84 Safari/537.36", - "expect": { - "vendor": "LG", - "model": "VK700", - "type": "tablet" - } - }, - { - "desc": "LG LK Series Tablet", - "ua": "Mozilla/5.0 (Linux; Android 5.0.1; LGLK430 Build/LRX21Y) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Safari/537.36", - "expect": { - "vendor": "LG", - "model": "LK430", - "type": "tablet" - } - }, - { - "desc": "Amazon Alexa Echo Show", - "ua": "AlexaWebMediaPlayer/1.0.200641.0 (Linux;Android 5.1.1)", - "expect": { - "vendor": "Amazon", - "model": "Alexa", - "type": "tablet" - } - }, - { - "desc": "Amazon Kindle Fire Tablet", - "ua": "Mozilla/5.0 (Linux; U; Android 4.4.3; en-us; KFSAWI Build/KTU84M) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.66 like Chrome/39.0.2171.93 Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "KFSAWI", - "type": "tablet" - } - }, - { - "desc": "Amazon Kindle Fire Tablet", - "ua": "Mozilla/5.0 (Linux; U; Android 4.4.3; en-us; KFSAWI) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.66 like Chrome/39.0.2171.93 Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "KFSAWI", - "type": "tablet" - } - }, - { - "desc": "Amazon Kindle Fire Tablet", - "ua": "Mozilla/5.0 (Linux; Android 9; KFMAWI Build/PS7312; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "KFMAWI", - "type": "tablet" - } - }, - { - "desc": "Amazon Fire TV", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; AFTB Build/JDQ39) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.173 Mobile Safari/537.22", - "expect": { - "vendor": "Amazon", - "model": "B", - "type": "smarttv" - } - }, - { - "desc": "Amazon Fire TV", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; AFTT) AppleWebKit/537.36 (KHTML, like Gecko) Silk/86.3.20 like Chrome/86.0.4240.198 Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "T", - "type": "smarttv" - } - }, - { - "desc": "Amazon Fire TV", - "ua": "Mozilla/5.0 (Linux; Android 9; AFTKA Build/PS7633.3445N; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.160 Mobile Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "KA", - "type": "smarttv" - } - }, - { - "desc": "Android TV", - "ua": "Mozilla/5.0 (Linux; Android 10; 2020/2021 UHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "smarttv" - } - }, - { - "desc": "Amazon Fire 7", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; KFAUWI) AppleWebKit/537.36 (KHTML, like Gecko) Silk/80.5.3 like Chrome/80.0.3987.162 Safari/537.36", - "expect": { - "vendor": "Amazon", - "model": "KFAUWI", - "type": "tablet" - } - }, - { - "desc": "FaceBook Mobile App", - "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", - "expect": { - "vendor": "Apple", - "model": "iPhone12,1", - "type": "mobile" - } - }, - { - "desc": "Issue #519", - "ua": "ios/iPhone/14.2/SOME_CUSTOM_APP_VERSION", - "expect": { - "vendor": "Apple", - "model": "iPhone", - "type": "mobile" - } - }, - { - "desc": "Issue #454", - "ua": "Mosamzilla/5.0 (Windows; U; Win98; en-US; rv:1.7.5) Gecko/20050603 Netscape/8.0.2", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "undefined" - } - }, - { - "desc": "Alcatel", - "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ALCATEL A564C Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36", - "expect": { - "vendor": "ALCATEL", - "model": "A564C", - "type": "mobile" - } - }, - { - "desc": "Alcatel Go Flip", - "ua": "Mozilla/5.0 (Mobile; ALCATEL4044T; rv:37.0) Gecko/37.0 Firefox/37.0 KaiOS/1.0", - "expect": { - "vendor": "ALCATEL", - "model": "4044T", - "type": "mobile" - } - }, - { - "desc": "Jolla", - "ua": "Mozilla/5.0 (Maemo; Linux; U; Jolla; Sailfish; Mobile; rv:31.0) Gecko/31.0 Firefox/31.0 SailfishBrowser/1.0", - "expect": { - "vendor": "Jolla", - "model": "undefined", - "type": "mobile" - } - }, - { - "desc": "Xbox One", - "ua": "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; Xbox; Xbox One)", - "expect": { - "vendor": "Microsoft", - "model": "Xbox One", - "type": "console" - } - }, - { - "desc": "Xbox", - "ua": "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; Xbox)", - "expect": { - "vendor": "Microsoft", - "model": "Xbox", - "type": "console" - } - }, - { - "desc": "Nvidia Shield Tablet", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SHIELD Tablet Build/LVY48E; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/45.0.2454.19 Safari/537.36", - "expect": { - "vendor": "Nvidia", - "model": "SHIELD Tablet", - "type": "tablet" - } - }, - { - "desc": "Ouya", - "ua": "Mozilla/5.0 (Linux; Android 4.1.2; OUYA Console Build/JZO54L-OUYA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.84 Safari/537.36", - "expect": { - "vendor": "OUYA", - "model": "undefined", - "type": "console" - } - }, - { - "desc": "Vivo S1 Pro", - "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1920) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Vivo", - "model": "1920", - "type": "mobile" - } - }, - { - "desc": "Vivo Y52s", - "ua": "Mozilla/5.0 (Linux; Android 10; V2057A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.10 SP-engine/2.28.0 baiduboxapp/12.10.0.10 (Baidu; P1 10) NABar/1.0", - "expect": { - "vendor": "Vivo", - "model": "V2057A", - "type": "mobile" - } - }, - { - "desc": "Vivo X60", - "ua": "Mozilla/5.0 (Linux; Android 11; V2046A; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/8.8.71.0", - "expect": { - "vendor": "Vivo", - "model": "V2046A", - "type": "mobile" - } - }, - { - "desc": "Vivo Y79A", - "ua": "Mozilla/5.0 (Linux; Android 7.1.2; vivo Y79A Build/N2G47H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.0.14.0", - "expect": { - "vendor": "Vivo", - "model": "Y79A", - "type": "mobile" - } - }, - { - "desc": "Vivo Y93", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; vivo 1814) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Vivo", - "model": "1814", - "type": "mobile" - } - }, - { - "desc": "Vivo Y97", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; V1813T Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.0.14.0", - "expect": { - "vendor": "Vivo", - "model": "V1813T", - "type": "mobile" - } - }, - { - "desc": "Vivo iQOO Pro", - "ua": "Mozilla/5.0 (Linux; Android 11; V1916A; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.1.10.6", - "expect": { - "vendor": "Vivo", - "model": "V1916A", - "type": "mobile" - } - }, - { - "desc": "Vivo 1906 (Y11)", - "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1906) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "Vivo", - "model": "1906", - "type": "mobile" - } - }, - { - "desc": "Unknown Mobile using Firefox", - "ua": "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "mobile" - } - }, - { - "desc": "Unknown Tablet using Firefox", - "ua": "Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "tablet" - } - }, - { - "desc": "Unknown Mobile using Focus for Android", - "ua": "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Focus/1.0 Chrome/59.0.3029.83 Mobile Safari/537.36", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "mobile" - } - }, - { - "desc": "Unknown Tablet using Focus for Android", - "ua": "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Focus/1.0 Chrome/59.0.3029.83 Safari/537.36", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "tablet" - } - }, - { - "desc": "Unknown Device using Focus for Android with GeckoView", - "ua": "Mozilla/5.0 (Android 7.0; Mobile; rv:62.0) Gecko/62.0 Firefox/62.0", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "mobile" - } - }, - { - "desc": "Unknown Mobile using Firefox OS", - "ua": "Mozilla/5.0 (Mobile; rv:26.0) Gecko/26.0 Firefox/26.0", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "mobile" - } - }, - { - "desc": "Unknown Tablet using Firefox OS", - "ua": "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "tablet" - } - }, - { - "desc": "Unknown TV using Firefox OS", - "ua": "Mozilla/5.0 (TV; rv:44.0) Gecko/44.0 Firefox/44.0", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "smarttv" - } - }, - { - "desc": "PDA with Windows CE", - "ua": "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", - "expect": { - "vendor": "undefined", - "model": "undefined", - "type": "mobile" - } - } -] \ No newline at end of file diff --git a/test/specs/devices/_others.json b/test/specs/devices/_others.json new file mode 100644 index 000000000..7ee9ec649 --- /dev/null +++ b/test/specs/devices/_others.json @@ -0,0 +1,250 @@ +[ + { + "desc": "Generic Android Device", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; i980 Build/MRA58K)", + "expect": { + "vendor": "Generic", + "model": "Android 6.0.1" + } + }, + { + "desc": "K", + "ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "K", + "type": "mobile" + } + }, + { + "desc": "Desktop (IE11 with Tablet string)", + "ua": "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; GWX:MANAGED; rv:11.0) like Gecko", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "undefined" + } + }, + { + "desc": "Mobile (DuckDuckGo mobile browser)", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile DuckDuckGo/5 Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "JVC LT-43V55LFA Smart TV", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB211 VSTVB MB200 HbbTV/1.2.1 (; JVC; MB211; 3.19.4.2; _TV_NT72563_2017 SmartTvA/3.0.0", + "expect": { + "vendor": "JVC", + "model": "MB211", + "type": "smarttv" + } + }, + { + "desc": "JVC LT-43V65LUA Smart TV", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB130 VSTVB MB100 HbbTV/1.2.1 (; JVC; MB130; 5.7.20.0; _TV_G10_2017;) SmartTvA/3.0.0", + "expect": { + "vendor": "JVC", + "model": "MB130", + "type": "smarttv" + } + }, + { + "desc": "Loewe Smart TV", + "ua": "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", + "expect": { + "vendor": "LOEWE", + "model": "SL410", + "type": "smarttv" + } + }, + { + "desc": "Issue #747", + "ua": "python-requests/2.25.1", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "undefined" + } + }, + { + "desc": "Issue #454", + "ua": "Mosamzilla/5.0 (Windows; U; Win98; en-US; rv:1.7.5) Gecko/20050603 Netscape/8.0.2", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "undefined" + } + }, + { + "desc": "OnePlus 7T Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36 EdgA/110.0.1587.66", + "expect": { + "vendor": "undefined", + "model": "HD1913", + "type": "mobile" + } + }, + { + "desc": "Philips SmartTV", + "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PFL6606K/02 SmartTV (2011)", + "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PFL6606K/02 SmartTV (2013)", + "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PHS5301/12 SmartTV (2016)", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36 OPR/29.0.1803.0 OMI/4.5.23.37.MOT2.13 HbbTV/1.2.1 (;Philips;32PHS5301/12;;_TV_MT5800;) Firmware/TPM161E_012.002.045.001 en", + "expect": { + "vendor": "Philips", + "model": "32PHS5301/12", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV", + "ua": "Mozilla/5.0 (SMART-TV; X11; Linux armv7l) AppleWebkit/537.42 (KHTML, like Gecko) Safari/537.42", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Android Phone Unidentified Vendor (docomo F-04K)", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; F-04K Build/V15R060P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", + "expect": { + "model": "F-04K", + "type": "mobile" + } + }, + { + "desc": "Android Tablet Unidentified Vendor (docomo F-02K)", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; F-02K Build/V44R059G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36", + "expect": { + "model": "F-02K", + "type": "tablet" + } + }, + { + "desc": "Android Tablet Unidentified Vendor (docomo d-02K)", + "ua": "Mozilla/5.0 (Linux; Android 9; d-02K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Safari/537.36", + "expect": { + "model": "d-02K", + "type": "tablet" + } + }, + { + "desc": "Android TV", + "ua": "Mozilla/5.0 (Linux; Android 10; 2020/2021 UHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Unknown Mobile using Firefox", + "ua": "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "Unknown Tablet using Firefox", + "ua": "Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "tablet" + } + }, + { + "desc": "Unknown Mobile using Focus for Android", + "ua": "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Focus/1.0 Chrome/59.0.3029.83 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "Unknown Tablet using Focus for Android", + "ua": "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Focus/1.0 Chrome/59.0.3029.83 Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "tablet" + } + }, + { + "desc": "Unknown Device using Focus for Android with GeckoView", + "ua": "Mozilla/5.0 (Android 7.0; Mobile; rv:62.0) Gecko/62.0 Firefox/62.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "Unknown Mobile using Firefox OS", + "ua": "Mozilla/5.0 (Mobile; rv:26.0) Gecko/26.0 Firefox/26.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "Unknown Tablet using Firefox OS", + "ua": "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "tablet" + } + }, + { + "desc": "Unknown TV using Firefox OS", + "ua": "Mozilla/5.0 (TV; rv:44.0) Gecko/44.0 Firefox/44.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "PDA with Windows CE", + "ua": "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/acer.json b/test/specs/devices/acer.json new file mode 100644 index 000000000..7eff9d80e --- /dev/null +++ b/test/specs/devices/acer.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Acer Iconia A1-810", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; A1-810 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36", + "expect": { + "vendor": "Acer", + "model": "A1-810", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/advan.json b/test/specs/devices/advan.json new file mode 100644 index 000000000..16a233106 --- /dev/null +++ b/test/specs/devices/advan.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Advan M4", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; ADVAN M4 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36 OPR/28.0.2254.119214", + "expect": { + "vendor": "ADVAN", + "model": "M4", + "type": "mobile" + } + }, + { + "desc": "Advan S40", + "ua": "Mozilla/5.0 (Linux; Android 7.0; ADVAN S40 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Mobile Safari/537.36 EdgA/79.0.309.58", + "expect": { + "vendor": "ADVAN", + "model": "S40", + "type": "mobile" + } + }, + { + "desc": "Advan Sketsa 2", + "ua": "Mozilla/5.0 (Linux; Android 11; ADVAN 1011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Safari/537.36", + "expect": { + "vendor": "ADVAN", + "model": "1011", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/alcatel.json b/test/specs/devices/alcatel.json new file mode 100644 index 000000000..edfdfcda3 --- /dev/null +++ b/test/specs/devices/alcatel.json @@ -0,0 +1,20 @@ +[ + { + "desc": "Alcatel", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ALCATEL A564C Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36", + "expect": { + "vendor": "ALCATEL", + "model": "A564C", + "type": "mobile" + } + }, + { + "desc": "Alcatel Go Flip", + "ua": "Mozilla/5.0 (Mobile; ALCATEL4044T; rv:37.0) Gecko/37.0 Firefox/37.0 KaiOS/1.0", + "expect": { + "vendor": "ALCATEL", + "model": "4044T", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/amazon.json b/test/specs/devices/amazon.json new file mode 100644 index 000000000..6e60318e8 --- /dev/null +++ b/test/specs/devices/amazon.json @@ -0,0 +1,137 @@ +[ + { + "desc": "Kindle Fire HD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.4 Mobile Safari/535.19 Silk-Accelerated=true", + "expect": { + "vendor": "Amazon", + "model": "KFTT", + "type": "tablet" + } + }, + { + "desc": "Kindle Fire HD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.4 Mobile Safari/535.19 Silk-Accelerated=true", + "expect": { + "vendor": "Amazon", + "model": "KFTT", + "type": "tablet" + } + }, + { + "desc": "Echo Show 5", + "ua": "Mozilla/5.0 (Linux; Android 5.1; AEORK Build/LVY48F; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEORK", + "type": "tablet" + } + }, + { + "desc": "Echo Show 8", + "ua": "Mozilla/5.0 (Linux; Android 7.1; AEOCH) AppleWebKit/537.36 (KHTML, like Gecko) Silk/77.2.21 like Chrome/77.0.3865.92 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOCH", + "type": "tablet" + } + }, + { + "desc": "Echo Show 8", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; AEOCW) AppleWebKit/537.36 (KHTML, like Gecko) Silk/106.3.3 like Chrome/106.0.5249.170 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOCW", + "type": "tablet" + } + }, + { + "desc": "Echo Show 15", + "ua": "Mozilla/5.0 (Linux; Android 9; AEOHY) AppleWebKit/537.36 (KHTML, like Gecko) Silk/112.6.3 like Chrome/112.0.5615.213 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOHY", + "type": "tablet" + } + }, + { + "desc": "Echo Dot", + "ua": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEOBC Build/LVY48F)", + "expect": { + "vendor": "Amazon", + "model": "AEOBC", + "type": "embedded" + } + }, + { + "desc": "Amazon Kindle Fire Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.3; en-us; KFSAWI Build/KTU84M) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.66 like Chrome/39.0.2171.93 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KFSAWI", + "type": "tablet" + } + }, + { + "desc": "Amazon Kindle Fire Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.3; en-us; KFSAWI) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.66 like Chrome/39.0.2171.93 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KFSAWI", + "type": "tablet" + } + }, + { + "desc": "Amazon Kindle Fire Tablet", + "ua": "Mozilla/5.0 (Linux; Android 9; KFMAWI Build/PS7312; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KFMAWI", + "type": "tablet" + } + }, + { + "desc": "Amazon Fire TV", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; AFTB Build/JDQ39) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.173 Mobile Safari/537.22", + "expect": { + "vendor": "Amazon", + "model": "B", + "type": "smarttv" + } + }, + { + "desc": "Amazon Fire TV", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; AFTT) AppleWebKit/537.36 (KHTML, like Gecko) Silk/86.3.20 like Chrome/86.0.4240.198 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "T", + "type": "smarttv" + } + }, + { + "desc": "Amazon Fire TV", + "ua": "Mozilla/5.0 (Linux; Android 9; AFTKA Build/PS7633.3445N; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.160 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KA", + "type": "smarttv" + } + }, + { + "desc": "Amazon Fire 7", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; KFAUWI) AppleWebKit/537.36 (KHTML, like Gecko) Silk/80.5.3 like Chrome/80.0.3987.162 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KFAUWI", + "type": "tablet" + } + }, + { + "desc": "Amazon Alexa Echo Show", + "ua": "AlexaWebMediaPlayer/1.0.200641.0 (Linux;Android 5.1.1)", + "expect": { + "vendor": "Amazon", + "model": "Alexa", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/apple.json b/test/specs/devices/apple.json new file mode 100644 index 000000000..4de572889 --- /dev/null +++ b/test/specs/devices/apple.json @@ -0,0 +1,128 @@ +[ + { + "desc": "Apple Desktop", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15", + "expect": { + "vendor": "Apple", + "model": "Macintosh", + "type": "undefined" + } + }, + { + "desc": "Apple Watch", + "ua": "atc/1.0 watchOS/7.3.3 model/Watch4,2 hwp/t8006 build/18S830 (6; dt:191)", + "expect": { + "vendor": "Apple", + "model": "watch", + "type": "wearable" + } + }, + { + "desc": "iPad using UCBrowser", + "ua": "Mozilla/5.0 (iPad; U; CPU OS 11_2 like Mac OS X; zh-CN; iPad5,3) AppleWebKit/534.46 (KHTML, like Gecko) UCBrowser/3.0.1.776 U3/ Mobile/10A403 Safari/7543.48.3", + "expect": { + "vendor": "Apple", + "model": "iPad", + "type": "tablet" + } + }, + { + "desc": "iPad Air", + "ua": "Mozilla/5.0 (iPad; CPU OS 12_4_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPad4,1;FBMD/iPad;FBSN/iOS;FBSV/12.4.5;FBSS/2;FBID/tablet;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPad", + "type": "tablet" + } + }, + { + "desc": "iPad using Facebook Browser", + "ua": "Mozilla/5.0 (iPad; CPU OS 14_4_2 like Mac OS X) WebKit/8610 (KHTML, like Gecko) Mobile/18D70 [FBAN/FBIOS;FBDV/iPad7,11;FBMD/iPad;FBSN/iOS;FBSV/14.4.2;FBSS/2;FBID/tablet;FBLC/en_US;FBOP/5]", + "expect": { + "vendor": "Apple", + "model": "iPad", + "type": "tablet" + } + }, + { + "desc": "iPod", + "ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53", + "expect": { + "vendor": "Apple", + "model": "iPod touch", + "type": "mobile" + } + }, + { + "desc": "iPhone", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPhone SE", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPhone SE using Facebook App", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPhone 11 Pro Max", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone12,5;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPhone XS", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone11,2;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPod touch", + "ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A501 Safari/9537.53", + "expect": { + "vendor": "Apple", + "model": "iPod touch", + "type": "mobile" + } + }, + { + "desc": "FaceBook Mobile App", + "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", + "expect": { + "vendor": "Apple", + "model": "iPhone12,1", + "type": "mobile" + } + }, + { + "desc": "Issue #519", + "ua": "ios/iPhone/14.2/SOME_CUSTOM_APP_VERSION", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/asus.json b/test/specs/devices/asus.json new file mode 100644 index 000000000..4e3a0ef2a --- /dev/null +++ b/test/specs/devices/asus.json @@ -0,0 +1,155 @@ +[ + { + "desc": "ASUS Nexus 7", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "Nexus 7", + "type": "tablet" + } + }, + { + "desc": "ASUS Padfone", + "ua": "Mozilla/5.0 (Linux; Android 4.1.1; PadFone 2 Build/JRO03L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "PadFone", + "type": "tablet" + } + }, + { + "desc": "ASUS ZenPad 10", + "ua": "Mozilla/5.0 (Linux; Android 6.0; P00C Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "P00C", + "type": "tablet" + } + }, + { + "desc": "ASUS ZenPad Z8s", + "ua": "Mozilla/5.0 (Linux; Android 7.0; ASUS_P00J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Safari/537.36\n", + "expect": { + "vendor": "ASUS", + "model": "P00J", + "type": "tablet" + } + }, + { + "desc": "ASUS ROG", + "ua": "Mozilla/5.0 (Linux; Android 8.1; ZS600KL Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZS600KL", + "type": "mobile" + } + }, + { + "desc": "ASUS ROG II", + "ua": "Mozilla/5.0 (Linux; Android 9; ASUS_I001DA Build/PKQ1.190414.001; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "I001DA", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 2", + "ua": "Mozilla/5.0 (Linux; Android 5.0; ASUS ZenFone 2 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZenFone 2", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 3 Deluxe", + "ua": "Mozilla/5.0 (Linux; Android 6.0; ASUS_Z016D Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.132 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "Z016D", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 5", + "ua": "Mozilla/5.0 (Linux; Android 8.0; ZE620KL Build/OPR1.170623.032) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.158 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZE620KL", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 7", + "ua": "Mozilla/5.0 (Linux; Android 10; ASUS_I002D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.81 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "I002D", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; ZS671KS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZS671KS", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Max Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; ZB602KL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZB602KL", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Max Pro (M1)", + "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X00TD Build/OPM1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "X00TD", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Max M2", + "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X01AD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "X01AD", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Max Pro M2", + "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X01BDA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "X01BDA", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Go", + "ua": "Mozilla/5.0 (Linux; Android 6.0; ASUS_X009DA Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "X009DA", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 2 Laser", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; ASUS_Z00ED) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "Z00ED", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/blackberry.json b/test/specs/devices/blackberry.json new file mode 100644 index 000000000..f809d5541 --- /dev/null +++ b/test/specs/devices/blackberry.json @@ -0,0 +1,47 @@ +[ + { + "desc": "BlackBerry Priv", + "ua": "User-Agent: Mozilla/5.0 (Linux; Android 5.1.1; STV100-1 Build/LMY47V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/46.0.2490.76 Mobile Safari/537.36", + "expect": { + "vendor": "BlackBerry", + "model": "STV100-1", + "type": "mobile" + } + }, + { + "desc": "BlackBerry Keyone", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BBB100-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.111 Mobile Safari/537.36", + "expect": { + "vendor": "BlackBerry", + "model": "BBB100-1", + "type": "mobile" + } + }, + { + "desc": "BlackBerry Key2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BBF100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36", + "expect": { + "vendor": "BlackBerry", + "model": "BBF100-1", + "type": "mobile" + } + }, + { + "desc": "BlackBerry Key2 LE", + "ua": "User-Agent: Mozilla/5.0 (Linux; Android 8.1.0; BBE100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497 Mobile Safari/537.36", + "expect": { + "vendor": "BlackBerry", + "model": "BBE100-1", + "type": "mobile" + } + }, + { + "desc": "Blackview 4900Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; BV4900Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "BV4900Pro", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/cat.json b/test/specs/devices/cat.json new file mode 100644 index 000000000..7c5710d30 --- /dev/null +++ b/test/specs/devices/cat.json @@ -0,0 +1,38 @@ +[ + { + "desc": "Cat B15Q", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; B15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "Cat", + "model": "B15Q", + "type": "mobile" + } + }, + { + "desc": "Cat B35", + "ua": "Mozilla/5.0 (Mobile; CAT B35; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5.1", + "expect": { + "vendor": "Cat", + "model": "B35", + "type": "mobile" + } + }, + { + "desc": "Cat S22 Flip", + "ua": "Mozilla/5.0 (Linux; Android 11; S22 FLIP Build/RKQ1.210416.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.165 Mobile Safari/537.36", + "expect": { + "vendor": "Cat", + "model": "S22 FLIP", + "type": "mobile" + } + }, + { + "desc": "Cat S62 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; S62 Pro Build/RKQ1.210406.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/94.0.4606.85 Mobile Safari/537.36 GSA/12.34.17.23.arm64", + "expect": { + "vendor": "Cat", + "model": "S62 Pro", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/energizer.json b/test/specs/devices/energizer.json new file mode 100644 index 000000000..ec1ec4762 --- /dev/null +++ b/test/specs/devices/energizer.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Energizer Energy 400", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Energy400 Build/MRA58K test-keys; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.158 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/172.0.0.66.93;]", + "expect": { + "vendor": "Energizer", + "model": "Energy400", + "type": "mobile" + } + }, + { + "desc": "Energizer Energy 400S", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Energy 400S Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.85 Mobile Safari/537.36", + "expect": { + "vendor": "Energizer", + "model": "Energy 400S", + "type": "mobile" + } + }, + { + "desc": "Energizer Ultimate 65G", + "ua": "Mozilla/5.0 (Linux; Android 14; Energizer Ultimate 65G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Energizer", + "model": "Ultimate 65G", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/facebook.json b/test/specs/devices/facebook.json new file mode 100644 index 000000000..01e1c97ec --- /dev/null +++ b/test/specs/devices/facebook.json @@ -0,0 +1,38 @@ +[ + { + "desc": "Oculus Quest", + "ua": "Mozilla/5.0 (Linux; Android 10; Quest) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/15.0.0.0.22.280317669 SamsungBrowser/4.0 Chrome/89.0.4389.90 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest", + "type": "xr" + } + }, + { + "desc": "Oculus Quest 2", + "ua": "Mozilla/5.0 (Linux; Android 10; Quest 2) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/15.0.0.0.22.280317669 SamsungBrowser/4.0 Chrome/89.0.4389.90 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest 2", + "type": "xr" + } + }, + { + "desc": "Oculus Quest 3", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest 3) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/31.4.0.6.51.566757996 Chrome/120.0.6099.283 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest 3", + "type": "xr" + } + }, + { + "desc": "Oculus Quest Pro", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest Pro) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/24.4.0.22.60.426469926 SamsungBrowser/4.0 Chrome/106.0.5249.181 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest Pro", + "type": "xr" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/fairphone.json b/test/specs/devices/fairphone.json new file mode 100644 index 000000000..e341cf7b1 --- /dev/null +++ b/test/specs/devices/fairphone.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Fairphone 1U", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; FP1U Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Fairphone", + "model": "FP1U", + "type": "mobile" + } + }, + { + "desc": "Fairphone 2", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; FP2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Fairphone", + "model": "FP2", + "type": "mobile" + } + }, + { + "desc": "Fairphone 3", + "ua": "Mozilla/5.0 (Linux; Android 9; FP3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", + "expect": { + "vendor": "Fairphone", + "model": "FP3", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/google.json b/test/specs/devices/google.json new file mode 100644 index 000000000..a06b99127 --- /dev/null +++ b/test/specs/devices/google.json @@ -0,0 +1,236 @@ +[ + { + "desc": "Google Chromecast with Google TV", + "ua": "Mozilla/5.0 (Linux; Android 12.0; Build/STTL.240206.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.0 Safari/537.36 CrKey/1.56.500000 DeviceType/AndroidTV", + "expect": { + "vendor": "Google", + "model": "Chromecast AndroidTV", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Mini Smart Speaker", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/SmartSpeaker", + "expect": { + "vendor": "Google", + "model": "Chromecast SmartSpeaker", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Third Generation", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/Chromecast", + "expect": { + "vendor": "Google", + "model": "Chromecast Third Generation", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Nest Hub", + "ua": "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 CrKey/1.56.500000", + "expect": { + "vendor": "Google", + "model": "Chromecast Nest Hub", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.84 Safari/537.36 CrKey/1.22.79313", + "expect": { + "vendor": "Google", + "model": "Chromecast", + "type": "smarttv" + } + }, + { + "desc": "Google Pixel C", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel C", + "type": "tablet" + } + }, + { + "desc": "Google Pixel C", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.64 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel C", + "type": "tablet" + } + }, + { + "desc": "Google Pixel", + "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel Build/NDE63V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel", + "type": "mobile" + } + }, + { + "desc": "Google Pixel Tablet", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel Tablet Build/AP2A.240905.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel Tablet", + "type": "tablet" + } + }, + { + "desc": "Google Pixel Watch", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch Build/TWD4.231005.002)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch", + "type": "wearable" + } + }, + { + "desc": "Google Pixel Watch 2", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch 2 Build/TWD9.240605.001.A1)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch 2", + "type": "wearable" + } + }, + { + "desc": "Google Pixel XL", + "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 2", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2 XL", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 XL Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 2 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2 XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 2 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 2 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3 XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3 XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3a", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 3a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3a XL", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 3a XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3a XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 4", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.83 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 4a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4 XL", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 4 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.120 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 5", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7", + "ua": "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 7", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/hmd.json b/test/specs/devices/hmd.json new file mode 100644 index 000000000..2ca10a1ed --- /dev/null +++ b/test/specs/devices/hmd.json @@ -0,0 +1,29 @@ +[ + { + "desc": "HMD Pulse", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse", + "type": "mobile" + } + }, + { + "desc": "HMD Pulse Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse Plus) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse Plus", + "type": "mobile" + } + }, + { + "desc": "HMD Pulse Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse Pro", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/honor.json b/test/specs/devices/honor.json new file mode 100644 index 000000000..321e51b1f --- /dev/null +++ b/test/specs/devices/honor.json @@ -0,0 +1,164 @@ +[ + { + "desc": "Honor MagicPad 13 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; GDI-W09 Build/HONORGDI-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "GDI-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 2", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; en-nz; JDN-W09 Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/6.0 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "JDN-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 2", + "ua": "Mozilla/5.0 (Linux; U; Android 9; zh-Hans-CN; JDN2-W09HN Build/HUAWEIJDN2-W09HN) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/4.6.6.164 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "JDN2-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 7 10.1", + "ua": "Mozilla/5.0 (Linux; Android 12; AGM3-AL09HN Build/HONORAGM3-AL09HN; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.46 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/490.0.0.63.82;IABMV/1;]", + "expect": { + "vendor": "Honor", + "model": "AGM3-AL09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 8 12.0", + "ua": "Mozilla/5.0 (Linux; Android 12; HEY-W09 Build/HONORHEY-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "HEY-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 9 12.1", + "ua": "Mozilla/5.0 (Linux; Android 13; HEY2-N09 Build/HONORHEY2-N09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 [FB_IAB/FB4A;FBAV/465.0.0.63.83;]", + "expect": { + "vendor": "Honor", + "model": "HEY2-N09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 9 12.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; HEY2-W09 Build/HONORHEY2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.102 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", + "expect": { + "vendor": "Honor", + "model": "HEY2-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad V7 Pro 11", + "ua": "Mozilla/5.0 (Linux; Android 12; BRT-AN09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 EdgA/109.0.1518.53", + "expect": { + "vendor": "Honor", + "model": "BRT-AN09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad V7 Pro 11 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; BRT-W09 Build/HONORBRT-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.5.0.336 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "BRT-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X6", + "ua": "Mozilla/5.0 (Linux; Android 10; AGR-W09HN Build/HUAWEIAGR-W09HN; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.9 SP-engine/2.28.0 baiduboxapp/12.9.0.11 (Baidu; P1 10) NABar/1.0", + "expect": { + "vendor": "Honor", + "model": "AGR-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X7 8 LTE", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-AL00HN; HMSCore 6.0.0.306) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.3.300 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "KOB2-AL00HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X7 8 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-W09HN; HMSCore 6.1.0.314) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.0.0.301 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "KOB2-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X8 Lite", + "ua": "Mozilla/5.0 (Linux; Android 12; AGM-W09HN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "AGM-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X9 11.5 LTE", + "ua": "Mozilla/5.0 (Linux; Android 13; ELN-L09 Build/HONORELN-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Mobile Safari/537.36[FBAN/EMA;FBLC/zh_CN;FBAV/432.0.0.9.110;FBCX/modulariab;]", + "expect": { + "vendor": "Honor", + "model": "ELN-L09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X9 11.5 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 13; ELN-W09 Build/HONORELN-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "ELN-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei Honor 6A", + "ua": "Mozilla/5.0 (Linux; Android 7.0; DLI-L22 Build/HONORDLI-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/252.0.0.22.355;]", + "expect": { + "vendor": "Honor", + "model": "DLI-L22", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 7", + "ua": "Mozilla/5.0 (Linux; Android 6.0; PLK-L01 Build/HONORPLK-L01; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "PLK-L01", + "type": "mobile" + } + }, + { + "desc": "Huawei 10 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; HRY-LX1 Build/HONORHRY-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "HRY-LX1", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/htc.json b/test/specs/devices/htc.json new file mode 100644 index 000000000..51ff339ba --- /dev/null +++ b/test/specs/devices/htc.json @@ -0,0 +1,29 @@ +[ + { + "desc": "HTC Desire 820", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; HTC Desire 820 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36", + "expect": { + "vendor": "HTC", + "model": "Desire 820", + "type": "mobile" + } + }, + { + "desc": "HTC Evo Shift 4G", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Sprint APA7373KT Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0", + "expect": { + "vendor": "Sprint", + "model": "APA7373KT", + "type": "mobile" + } + }, + { + "desc": "HTC Nexus 9", + "ua": "Mozilla/5.0 (Linux; Android 5.0; Nexus 9 Build/LRX21R) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Mobile Crosswalk/7.36.154.13 Safari/537.36", + "expect": { + "vendor": "HTC", + "model": "Nexus 9", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/huawei.json b/test/specs/devices/huawei.json new file mode 100644 index 000000000..670879e66 --- /dev/null +++ b/test/specs/devices/huawei.json @@ -0,0 +1,947 @@ +[ + { + "desc": "Huawei Honor", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3; xx-xx; U8860 Build/HuaweiU8860) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "expect": { + "vendor": "Huawei", + "model": "U8860", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L41) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L41", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-AL10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-AL10", + "type": "mobile" + } + }, + { + "desc": "Huawei Nexus 6P", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 6P Build/MTC19V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Mobile Safari/537", + "expect": { + "vendor": "Huawei", + "model": "Nexus 6P", + "type": "mobile" + } + }, + { + "desc": "Huawei P10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; VTR-L09 Build/HUAWEIVTR-L09; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "VTR-L09", + "type": "mobile" + } + }, + { + "desc": "Huawei Y3II", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1; xx-xx; HUAWEI LUA-L03 Build/HUAWEILUA-L03) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LUA-L03", + "type": "mobile" + } + }, + { + "desc": "HUAWEI MediaPad C5 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; MON-AL19B Build/HUAWEIMON-AL19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.83 Mobile Safari/537.36 T7/11.7 baiduboxapp/11.7.0.10 (Baidu; P1 7.0)", + "expect": { + "vendor": "Huawei", + "model": "MON-AL19B", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M2 10.1", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; HUAWEI M2-A01L Build/HUAWEIM2-A01L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/95.0.4638.74 Safari/537.36[FBAN/EMA;FBLC/fr_FR;FBAV/421.0.0.14.100;]", + "expect": { + "vendor": "Huawei", + "model": "M2-A01L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; en-US; BTV-DL09 Build/HUAWEIBEETHOVEN-DL09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 UCBrowser/11.5.0.1015 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTV-DL09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; HUAWEI BTV-W09 Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.96 Mobile Safari/537.36 AlohaBrowser/3.1.1", + "expect": { + "vendor": "Huawei", + "model": "BTV-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 Lite", + "ua": "Mozilla/5.0 (Linux; Android 7.0; CPN-L09 Build/HUAWEICPN-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36[FBAN/EMA;FBLC/ru_RU;FBAV/233.0.0.12.118;]", + "expect": { + "vendor": "Huawei", + "model": "CPN-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 Lite", + "ua": "Mozilla/5.0 (Linux; Android 7.0; CPN-W09 Build/HUAWEICPN-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.71 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/374.0.0.20.109;]", + "expect": { + "vendor": "Huawei", + "model": "CPN-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 Lite 10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BAH-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 10.8", + "ua": "Mozilla/5.0 (Linux; Android 9; CMR-W09 Build/HUAWEICMR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.102 Safari/537.36 Line/14.18.1/IAB", + "expect": { + "vendor": "Huawei", + "model": "CMR-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 Lite", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; BAH2-W19 Build/HUAWEIBAH2-W19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH2-W19", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; JDN2-W09 Build/HUAWEIJDN2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.210 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/318.0.0.39.154;]", + "expect": { + "vendor": "Huawei", + "model": "JDN2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; JDN2-AL50 Build/HUAWEIJDN2-AL50; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.13.0 SP-engine/2.29.0 matrixstyle/0 lite baiduboxapp/5.8.0.10 (Baidu; P1 9) NABar/1.", + "expect": { + "vendor": "Huawei", + "model": "JDN2-AL50", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 8.4", + "ua": "Mozilla/5.0 (Linux; Android 9; SHT-W09 Build/HUAWEISHT-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.87 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SHT-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5", + "ua": "Mozilla/5.0 (Linux; Android 9; SHT-AL09 Build/HUAWEISHT-AL09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SHT-AL09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M6 10.8", + "ua": "Mozilla/5.0 (Linux; Android 14; SCM-W09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6612.143 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SCM-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M6 8.4", + "ua": "Mozilla/5.0 (Linux; Android 9; VRD-W09; HMSCore 6.14.0.321; GMSCore 22.26.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "VRD-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T5", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; AGS2-L09 Build/HUAWEIAGS2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.125 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS2-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10", + "ua": "Mozilla/5.0 (Linux; U; Android 10; en-US; AGR-L09 Build/HUAWEIAGR-L09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.3.8.1305 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGR-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10", + "ua": "Mozilla/5.0 (Linux; Android 10; AGR-W09 Build/HUAWEIAGR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGR-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10s", + "ua": "Mozilla/5.0 (Linux; Android 10; AGS3-W09 Build/HUAWEIAGS3-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS3-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T 8.0", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-L09 Build/HUAWEIKOB2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/396.0.0.21.104;]", + "expect": { + "vendor": "Huawei", + "model": "KOB2-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T 8.0", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-W09 Build/HUAWEIKOB2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 Mobile Safari/537.36 HuaweiBrowser/15.0.4.312 HMSCore/6.14.0.301", + "expect": { + "vendor": "Huawei", + "model": "KOB2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; T1-A21w Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Safari/537.36 SputnikBrowser/1.2.8.161", + "expect": { + "vendor": "Huawei", + "model": "T1-A21w", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; T1-A23L Build/HuaweiMediaPad; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.121 Mobile Safari/537.36 BingWeb/6.9.10", + "expect": { + "vendor": "Huawei", + "model": "T1-A23L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; T1-A21L Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "T1-A21L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 7", + "ua": "Mozilla/5.0 (Linux; 4.4.2; T1-701u) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "T1-701u", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 8", + "ua": "Mozilla/5.0 (Linux; U; Android 9.0; MediaPad T1 8.0 Build/HuaweiMediaPad) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 OPR/28.0.2254.119224", + "expect": { + "vendor": "Huawei", + "model": "MediaPad T1 8.0", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10 9.7", + "ua": "Mozilla/5.0 (Linux; U; Android 10; en-US; AGRK-L09 Build/HUAWEIAGRK-L09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.6.0.1315 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGRK-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10 9.7 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; AGRK-W09; HMSCore 6.14.0.321) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGRK-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10s 10.1 LTE", + "ua": "Mozilla/5.0 (Linux; Android 10; AGS3K-L09 Build/HUAWEIAGS3K-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36 [FB_IAB/FB4A;FBAV/362.0.0.27.109;]", + "expect": { + "vendor": "Huawei", + "model": "AGS3K-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10s 10.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; AGS3K-W09; HMSCore 6.14.0.321) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS3K-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T2 10.0 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; 605HW Build/HuaweiMediaPad; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.5005.78 Safari/537.36 [FB_IAB/FB4A;FBAV/436.0.0.35.101;]", + "expect": { + "vendor": "Huawei", + "model": "605HW", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T2 7.0 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0; BGO-DL09 Build/HuaweiBAGGIO; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/407.0.0.30.97;]", + "expect": { + "vendor": "Huawei", + "model": "BGO-DL09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; AGS-W09 Build/HUAWEIAGS-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.90 Safari/537.36 GSA/10.83.10.21.arm64", + "expect": { + "vendor": "Huawei", + "model": "AGS-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 7", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BG2-U03 Build/HUAWEIBG2-U03; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.111 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BG2-U03", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; KOB-W09 Build/HUAWEIKOB-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Safari/537.36 [FB_IAB/Orca-Android;FBAV/354.0.0.10.113;]", + "expect": { + "vendor": "Huawei", + "model": "KOB-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T5 10", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; AGS2-W09 Build/HUAWEIAGS2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Huawei", + "model": "AGS2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad X2", + "ua": "Mozilla/5.0 (Linux; Android 8.0; GEM-703L Build/HUAWEIGEM-703L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/043906 Mobile Safari/537.36 MicroMessenger/6.6.3.1260(0x26060339) NetType/WIFI Language/zh_", + "expect": { + "vendor": "Huawei", + "model": "GEM-703L", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH3-W09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH3-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH3-L09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH3-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; BAH3-W59 Build/HUAWEIBAH3-W59; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36HiSearch/22.0.6.315", + "expect": { + "vendor": "Huawei", + "model": "BAH3-W59", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 (2022)", + "ua": "Mozilla/5.0 (Linux; Android 10; BAH4-L09 Build/HUAWEIBAH4-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH4-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 (2022) WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH4-W09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH4-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 SE", + "ua": "Mozilla/5.0 (Linux; Android 12; AGS5-L09 Build/HUAWEIAGS5-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS5-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 SE WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; AGS5-W09 Build/HUAWEIAGS5-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Safari/537.36 [FB_IAB/FB4A;FBAV/480.0.0.54.88;]", + "expect": { + "vendor": "Huawei", + "model": "AGS5-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11 (2023) WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; DBR-W10 Build/HUAWEIDBR-W10) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.9.6.501 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DBR-W10", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-cn; DBY-W09 Build/HUAWEIDBY-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.86 MQQBrowser/14.6 Mobile Safari/537.36 COVC/046801", + "expect": { + "vendor": "Huawei", + "model": "DBY-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 Air WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; DBY2-W00 Build/HUAWEIDBY2-W00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/7.3.8.663 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DBY2-W00", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 LTE", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; BTK-AL09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTK-AL09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 S WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; TGR-W09; HMSCore 6.14.0.322; GMSCore 0.3.3.1.240913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 HuaweiBrowser/14.0.2.317 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "TGR-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; BTK-W09; HMSCore 6.14.0.322; GMSCore 214816056) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTK-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad C5 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; MON-W19 Build/HUAWEIMON-W19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36 [Pinterest/Android]", + "expect": { + "vendor": "Huawei", + "model": "MON-W19", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; GOT-AL09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 YaBrowser/23.5.5.60.01 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "GOT-AL09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 11 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; GOT-W09 Build/HUAWEIGOT-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Safari/537.36 T7/13.19 BDOS/1.0 (HarmonyOS 3.0.0) SP-engine/2.57.0 baiduboxapp/13.19.0.12 (Baidu; P1 12) NABar/1.0", + "expect": { + "vendor": "Huawei", + "model": "GOT-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 12.6 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; WGR-W09 Build/HUAWEIWGR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/412.0.0.8.106;]", + "expect": { + "vendor": "Huawei", + "model": "WGR-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad SE 11 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; AGS6-W09; HMSCore 6.12.2.309) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.5.315 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS6-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 13.2", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; PCE-W29; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "PCE-W29", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T 10", + "ua": "Mozilla/5.0 (Linux; Android 10; AGR-L09; HMSCore 5.0.4.301) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 HuaweiBrowser/11.0.3.304 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGR-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T10s", + "ua": "Mozilla/5.0 (Linux; U; Android 10; zh-cn; AGS3-AL00 Build/HUAWEIAGS3-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/11.4 Mobile Safari/537.36 COVC/045530", + "expect": { + "vendor": "Huawei", + "model": "AGS3-AL00", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T10s WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 10; AGS3-W09 Build/HUAWEIAGS3-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.93 Safari/537.36 OPR/60.0.2254.59405", + "expect": { + "vendor": "Huawei", + "model": "AGS3-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T8 8 LTE", + "ua": "Mozilla/5.0 (Linux; U; Android 10; KOB2K-L09 Build/HUAWEIKOB2K-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36 OPR/83.0.2254.73002", + "expect": { + "vendor": "Huawei", + "model": "KOB2K-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei M3", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BTV-W09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTV-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei Mate 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 8.0; BLA-L29 Build/HUAWEIBLA-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3236.6 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BLA-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate X", + "ua": "Mozilla/5.0 (Linux; Android 9; TAH-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.111 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "TAH-AN00", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate X2", + "ua": "Mozilla/5.0 (Linux; Android 10; TET-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "TET-AN00", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 X", + "ua": "Mozilla/5.0 (Linux; Android 9; EVR-L29 Build/HUAWEIEVR-L29; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "EVR-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-L09", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-AL00", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-AL10", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L0C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-L0C", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-TL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-TL00", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 50 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; DCO-LX9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DCO-LX9", + "type": "mobile" + } + }, + { + "desc": "Huawei P20 Lite", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "ANE-LX1", + "type": "mobile" + } + }, + { + "desc": "Huawei P20", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; EML-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "EML-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei P20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; CLT-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "CLT-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei P30", + "ua": "Mozilla/5.0 (Linux; Android 9; ELE-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "ELE-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei P30 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; VOG-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "VOG-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei P40", + "ua": "Mozilla/5.0 (Linux; Android 10; ANA-AN00 Build/HUAWEIANA-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/11.26 SP-engine/2.22.0 baiduboxapp/11.26.0.10 (Baidu; P1 10) NABar/1.0", + "expect": { + "vendor": "Huawei", + "model": "ANA-AN00", + "type": "mobile" + } + }, + { + "desc": "Huawei P40 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; ELS-AN00 Build/HUAWEIELS-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36 mailapp/6.0.0", + "expect": { + "vendor": "Huawei", + "model": "ELS-AN00", + "type": "mobile" + } + }, + { + "desc": "Huawei 30 Pro+", + "ua": "Mozilla/5.0 (Linux; Android 10; EBG-AN10 Build/HUAWEIEBG-AN10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.86 Mobile Safari/537.36 EdgA/42.0.0.2741", + "expect": { + "vendor": "Huawei", + "model": "EBG-AN10", + "type": "mobile" + } + }, + { + "desc": "Huawei 30S", + "ua": "Mozilla/5.0 (Linux; Android 10; CDY-AN90 Build/HUAWEICDY-AN90; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36 mailapp/5.8.0", + "expect": { + "vendor": "Huawei", + "model": "CDY-AN90", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L21", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L61", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L71) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L71", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L61D", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YALE-L61A", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YALE-L61D", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L71A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YALE-L71A", + "type": "mobile" + } + }, + { + "desc": "Huawei Enjoy10e", + "ua": "Dalvik/2.1.0 (Linux; U; Android 10; MED-AL00 Build/HUAWEIMED-AL00)", + "expect": { + "vendor": "Huawei", + "model": "MED-AL00", + "type": "mobile" + } + }, + { + "desc": "Huawei Y7 2018", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; LDN-L01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LDN-L01", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 8X", + "ua": "Mozilla/5.0 (Linux; Android 9; JSN-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "JSN-L21", + "type": "mobile" + } + }, + { + "desc": "Huawei Y6 2019", + "ua": "Mozilla/5.0 (Linux; Android 9; MRD-LX1N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "MRD-LX1N", + "type": "mobile" + } + }, + { + "desc": "Huawei Y9 2019", + "ua": "Mozilla/5.0 (Linux; Android 9; JKM-LX2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "JKM-LX2", + "type": "mobile" + } + }, + { + "desc": "Huawei Y5", + "ua": "Mozilla/5.0 (Linux; Android 9; AMN-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AMN-LX3", + "type": "mobile" + } + }, + { + "desc": "Huawei Y7p", + "ua": "Mozilla/5.0 (Linux; Android 9; ART-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "ART-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Lite", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; SNE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.116 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SNE-LX1", + "type": "mobile" + } + }, + { + "desc": "Huawei P10 Lite", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; WAS-LX1A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "WAS-LX1A", + "type": "mobile" + } + }, + { + "desc": "Huawei Y5 Lite 2018", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; DRA-LX5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DRA-LX5", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 8C", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BKK-LX2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BKK-LX2", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/imo.json b/test/specs/devices/imo.json new file mode 100644 index 000000000..e794a801b --- /dev/null +++ b/test/specs/devices/imo.json @@ -0,0 +1,38 @@ +[ + { + "desc": "IMO FEEL A2", + "ua": "Mozilla/5.0 (Linux; Android 5.1; IMO FEEL A2 Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/49.0.2623.105 Mobile Safari/537.36", + "expect": { + "vendor": "IMO", + "model": "FEEL A2", + "type": "mobile" + } + }, + { + "desc": "IMO Q2", + "ua": "Mozilla/5.0 (Linux; Android 5.1; IMO Q2 Build/LMY47D; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Mobile Safari/537.36 GSA/7.22.24.21.arm", + "expect": { + "vendor": "IMO", + "model": "Q2", + "type": "mobile" + } + }, + { + "desc": "IMO S2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; IMO S2 Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.162 Mobile Safari/537.36", + "expect": { + "vendor": "IMO", + "model": "S2", + "type": "mobile" + } + }, + { + "desc": "IMO Tab X9", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; id-id; IMO TAB X9 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "IMO", + "model": "TAB X9", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/infinix.json b/test/specs/devices/infinix.json new file mode 100644 index 000000000..6c356d3c2 --- /dev/null +++ b/test/specs/devices/infinix.json @@ -0,0 +1,56 @@ +[ + { + "desc": "Infinix Hot 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; Infinix X625C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X625C", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 10T", + "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X689C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X689C", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 11s", + "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X6812 Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X6812", + "type": "mobile" + } + }, + { + "desc": "Infinix Smart 5", + "ua": "Mozilla/5.0 (Linux; Android 10; Infinix X657C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X657C", + "type": "mobile" + } + }, + { + "desc": "Infinix XPad", + "ua": "Mozilla/5.0 (Linux; Android 14; Infinix X1101B Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.99 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", + "expect": { + "vendor": "Infinix", + "model": "X1101B", + "type": "tablet" + } + }, + { + "desc": "Infinix Zero 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; Infinix X6815B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X6815B", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/itel.json b/test/specs/devices/itel.json new file mode 100644 index 000000000..109e6e0b5 --- /dev/null +++ b/test/specs/devices/itel.json @@ -0,0 +1,56 @@ +[ + { + "desc": "itel A25", + "ua": "Mozilla/5.0 (Linux; Android 9; itel L5002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.130 Mobile Safari/537.36 OPR/63.3.3216.58675", + "expect": { + "vendor": "itel", + "model": "L5002", + "type": "mobile" + } + }, + { + "desc": "itel A50", + "ua": "Mozilla/5.0 (Linux; U; Android 14; itel A667L Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Mobile Safari/537.36 OPR/83.1.2254.73239", + "expect": { + "vendor": "itel", + "model": "A667L", + "type": "mobile" + } + }, + { + "desc": "itel KidPad 1", + "ua": "Mozilla/5.0 (Linux; Android 10; Itel W7001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.101 Mobile Safari/537.36", + "expect": { + "vendor": "itel", + "model": "W7001", + "type": "tablet" + } + }, + { + "desc": "itel Pad One", + "ua": "Mozilla/5.0 (Linux; Android 12; itel P10001L Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.172 Safari/537.36", + "expect": { + "vendor": "itel", + "model": "P10001L", + "type": "tablet" + } + }, + { + "desc": "itel RS4", + "ua": "Mozilla/5.0 (Linux; Android 13; itel S666LN Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.165 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/468.1.0.56.78;]", + "expect": { + "vendor": "itel", + "model": "S666LN", + "type": "mobile" + } + }, + { + "desc": "itel Vision 2S", + "ua": "Mozilla/5.0 (Linux; Android 11; itel P651L Build/RP1A.201005.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 Mobile Safari/537.36", + "expect": { + "vendor": "itel", + "model": "P651L", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/jolla.json b/test/specs/devices/jolla.json new file mode 100644 index 000000000..622fea01f --- /dev/null +++ b/test/specs/devices/jolla.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Jolla", + "ua": "Mozilla/5.0 (Maemo; Linux; U; Jolla; Sailfish; Mobile; rv:31.0) Gecko/31.0 Firefox/31.0 SailfishBrowser/1.0", + "expect": { + "vendor": "Jolla", + "model": "undefined", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/kobo.json b/test/specs/devices/kobo.json new file mode 100644 index 000000000..6648a9a59 --- /dev/null +++ b/test/specs/devices/kobo.json @@ -0,0 +1,20 @@ +[ + { + "desc": "Kobo eReader", + "ua": "Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Kobo eReader Safari/538.1", + "expect": { + "vendor": "Kobo", + "model": "eReader", + "type": "tablet" + } + }, + { + "desc": "Kobo Touch", + "ua": "Mozilla/5.0 (Linux; U; Android 2.0; en-us;) AppleWebKit/538.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/538.1 (Kobo Touch 0377/4.20.14622)", + "expect": { + "vendor": "Kobo", + "model": "Touch", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/lenovo.json b/test/specs/devices/lenovo.json new file mode 100644 index 000000000..2ffc8863c --- /dev/null +++ b/test/specs/devices/lenovo.json @@ -0,0 +1,74 @@ +[ + { + "desc": "Lenovo Tab 2", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A7", + "type": "tablet" + } + }, + { + "desc": "Lenovo Phone", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Lenovo PB2-650M Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/311.0.0.44.117;]", + "expect": { + "vendor": "Lenovo", + "model": "PB2-650M", + "type": "mobile" + } + }, + { + "desc": "Lenovo Tab 3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo YT3-X90F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-X90F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Lenovo TB-X304F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X304F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Lenovo TAB 2 A7-30HC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A7", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 7 Essential", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Lenovo TB-7304X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7304X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X606F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 YaBrowser/20.9.4.99.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606F", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab S6000", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; IdeaTab S6000-H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 YaBrowser/18.11.1.1011.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "IdeaTab S6000-H", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/lg.json b/test/specs/devices/lg.json new file mode 100644 index 000000000..f0312eba5 --- /dev/null +++ b/test/specs/devices/lg.json @@ -0,0 +1,164 @@ +[ + { + "desc": "LG V40 ThinQ", + "ua": "Mozilla/5.0 (Linux; Android 9; LM-V405) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-V405", + "type": "mobile" + } + }, + { + "desc": "LG K30", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; LM-X410.F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-X410.F", + "type": "mobile" + } + }, + { + "desc": "LG K30", + "ua": "Mozilla/5.0 (Linux; Android 9; LM-X410.FGN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-X410.FGN", + "type": "mobile" + } + }, + { + "desc": "LG K40", + "ua": "Mozilla/5.0 (Linux; Android 10; LM-X420) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-X420", + "type": "mobile" + } + }, + { + "desc": "LG Stylo 4", + "ua": "Mozilla/5.0 (Linux; Android 10; LM-Q710(FGN)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "LM-Q710(FGN)", + "type": "mobile" + } + }, + { + "desc": "LG Stylo 5", + "ua": "Mozilla/5.0 (Linux; Android 9; LM-Q720) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-Q720", + "type": "mobile" + } + }, + { + "desc": "LG G7 ThinQ", + "ua": "Mozilla/5.0 (Linux; Android 9; LM-G710VM Build/PKQ1.181105.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-G710VM", + "type": "mobile" + } + }, + { + "desc": "LG K20", + "ua": "Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:111.0) Gecko/111.0 Firefox/111.0", + "expect": { + "vendor": "LG", + "model": "M255", + "type": "mobile" + } + }, + { + "desc": "LG K500", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; LG-K500 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "K500", + "type": "mobile" + } + }, + { + "desc": "LG Nexus 4", + "ua": "Mozilla/5.0 (Linux; Android 4.2.1; Nexus 4 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19", + "expect": { + "vendor": "LG", + "model": "Nexus 4", + "type": "mobile" + } + }, + { + "desc": "LG Nexus 4", + "ua": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; Google Nexus 4 - 4.3 - API 18 - 768x1280 Build/JLS36G) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "LG", + "model": "Nexus 4", + "type": "mobile" + } + }, + { + "desc": "LG Nexus 5", + "ua": "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19", + "expect": { + "vendor": "LG", + "model": "Nexus 5", + "type": "mobile" + } + }, + { + "desc": "LG Wing", + "ua": "Mozilla/5.0 (Linux; Android 10; LM-F100N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.101 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-F100N", + "type": "mobile" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (DirectFB; U; Linux mips; en) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) LG Browser (; LG NetCast.TV-2011)", + "expect": { + "vendor": "LG", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Linux; NetCast; U) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/53.0.2785 34 Safari/537.31 SmartTV/8.5", + "expect": { + "vendor": "LG", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "LG Android TV", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; LG Android TV Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "LG", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "LG VK Series Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; VK700 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.84 Safari/537.36", + "expect": { + "vendor": "LG", + "model": "VK700", + "type": "tablet" + } + }, + { + "desc": "LG LK Series Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; LGLK430 Build/LRX21Y) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LK430", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/meizu.json b/test/specs/devices/meizu.json new file mode 100644 index 000000000..e18fdf080 --- /dev/null +++ b/test/specs/devices/meizu.json @@ -0,0 +1,20 @@ +[ + { + "desc": "Meizu M5 Note", + "ua": "Mozilla/5.0 (Linux; Android 6.0; M5 Note Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043024 Safari/537.36 MicroMessenger/6.5.7.1040 NetType/WIFI Language/zh_CN", + "expect": { + "vendor": "Meizu", + "model": "M5 Note", + "type": "mobile" + } + }, + { + "desc": "Meizu M3S", + "ua": "Mozilla/5.0 (X11; Linux; Android 5.1; MZ-M3s Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrom/45.0.2454.94 Mobile Safari/537.36", + "expect": { + "vendor": "Meizu", + "model": "M3s", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/micromax.json b/test/specs/devices/micromax.json new file mode 100644 index 000000000..041642097 --- /dev/null +++ b/test/specs/devices/micromax.json @@ -0,0 +1,30 @@ +[ + + { + "desc": "Micromax Bharat 2 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; en-US; Micromax Q402Plus Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.12.9.1226 Mobile Safari/537.36", + "expect": { + "vendor": "Micromax", + "model": "Q402Plus", + "type": "mobile" + } + }, + { + "desc": "Micromax Canvas Infinity", + "ua": "Mozilla/5.0 (Linux; U; Android 7.1.2; en-US; Micromax HS2 Build/N2G47H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/13.2.0.1296 (SpeedMode) U4/1.0 UCWEB/2.0 Mobile Safari/534.30", + "expect": { + "vendor": "Micromax", + "model": "HS2", + "type": "mobile" + } + }, + { + "desc": "Micromax In 1b", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Micromax E7533 Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 OPR/54.0.2254.56148", + "expect": { + "vendor": "Micromax", + "model": "E7533", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/microsoft.json b/test/specs/devices/microsoft.json new file mode 100644 index 000000000..79c35111a --- /dev/null +++ b/test/specs/devices/microsoft.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Microsoft Lumia 950", + "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", + "expect": { + "vendor": "Microsoft", + "model": "Lumia 950", + "type": "mobile" + } + }, + { + "desc": "Microsoft Surface Duo", + "ua": "Dalvik/2.1.0 (Linux; U; Android 10; Surface Duo Build/2020.1014.61)", + "expect": { + "vendor": "Microsoft", + "model": "Surface Duo", + "type": "tablet" + } + }, + { + "desc": "Microsoft Lumia 950", + "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", + "expect": { + "vendor": "Microsoft", + "model": "Lumia 950", + "type": "mobile" + } + }, + { + "desc": "Xbox", + "ua": "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; Xbox)", + "expect": { + "vendor": "Microsoft", + "model": "Xbox", + "type": "console" + } + }, + { + "desc": "Xbox One", + "ua": "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; Xbox; Xbox One)", + "expect": { + "vendor": "Microsoft", + "model": "Xbox One", + "type": "console" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/motorola.json b/test/specs/devices/motorola.json new file mode 100644 index 000000000..af6a4c878 --- /dev/null +++ b/test/specs/devices/motorola.json @@ -0,0 +1,83 @@ +[ + { + "desc": "Motorola Moto X", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; XT1097 Build/KXE21.187-38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT1097", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto Z3 Play", + "ua": "Mozilla/5.0 (Linux; Android 9; Moto Z3 Play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto Z3 Play", + "type": "mobile" + } + }, + { + "desc": "Motorola Nexus 6", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.20 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Nexus 6", + "type": "mobile" + } + }, + { + "desc": "Motorola Droid RAZR 4G", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3; xx-xx; DROID RAZR 4G Build/6.5.1-73_DHD-11_M1-29) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "expect": { + "vendor": "Motorola", + "model": "DROID RAZR 4G", + "type": "mobile" + } + }, + { + "desc": "Motorola RAZR 2019", + "ua": "Mozilla/5.0 (Linux; Android 9; motorola razr) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.1 Chrome/75.0.3770.143 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "razr", + "type": "mobile" + } + }, + { + "desc": "Moto X", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Motorola", + "model": "XT1058", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto g(6) Play", + "ua": "Mozilla/5.0 (Linux; Android 9; moto g(6) play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "moto g(6) play", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto g(7) Supra", + "ua": "Mozilla/5.0 (Linux; Android 9; moto g(7) supra Build/PCOS29.114-134-2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/73.0.3683.90 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "moto g(7) supra", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Build/NDQS26.69-64-11-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto E (4)", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/nintendo.json b/test/specs/devices/nintendo.json new file mode 100644 index 000000000..eb879b322 --- /dev/null +++ b/test/specs/devices/nintendo.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Nintendo Switch", + "ua": "Mozilla/5.0 (Nintendo Switch; WifiWebAuthApplet) AppleWebKit/606.4 (KHTML, like Gecko) NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393", + "expect": { + "vendor": "Nintendo", + "model": "Switch", + "type": "console" + } + }, + { + "desc": "Nintendo WiiU", + "ua": "Mozilla/5.0 (Nintendo WiiU) AppleWebKit/536.30 (KHTML, like Gecko) NX/3.0.4.2.9 NintendoBrowser/4.2.0.11146.EU", + "expect": { + "vendor": "Nintendo", + "model": "WiiU", + "type": "console" + } + }, + { + "desc": "Nintendo Wii", + "ua": "Opera/9.10 (Nintendo Wii; U; ; 1621; en)", + "expect": { + "vendor": "Nintendo", + "model": "Wii", + "type": "console" + } + }, + { + "desc": "Nintendo 3DS", + "ua": "Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7610.EU", + "expect": { + "vendor": "Nintendo", + "model": "3DS", + "type": "console" + } + }, + { + "desc": "Nintendo 3DS", + "ua": "Mozilla/5.0 (New Nintendo 3DS like iPhone) AppleWebKit/536.30 (KHTML, like Gecko) NX/3.0.0.5.15 Mobile NintendoBrowser/1.3.10126.EU", + "expect": { + "vendor": "Nintendo", + "model": "3DS", + "type": "console" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/nokia.json b/test/specs/devices/nokia.json new file mode 100644 index 000000000..c8cf6a929 --- /dev/null +++ b/test/specs/devices/nokia.json @@ -0,0 +1,74 @@ +[ + { + "desc": "Nokia3xx", + "ua": "Nokia303/14.87 CLDC-1.1", + "expect": { + "vendor": "Nokia", + "model": "303", + "type": "mobile" + } + }, + { + "desc": "Nokia 3.2", + "ua": "Mozilla/5.0 (Linux; Android 10; Nokia 3.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "3.2", + "type": "mobile" + } + }, + { + "desc": "Nokia 7", + "ua": "Mozilla/5.0 (Linux; Android 11; Nokia 7.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "7.2", + "type": "mobile" + } + }, + { + "desc": "Nokia N9", + "ua": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + "expect": { + "vendor": "Nokia", + "model": "N9", + "type": "mobile" + } + }, + { + "desc": "Nokia T20", + "ua": "Mozilla/5.0 (Linux; Android 12; Nokia T20) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "T20", + "type": "tablet" + } + }, + { + "desc": "Nokia T20", + "ua": "Mozilla/5.0 (Linux; Android 11; Nokia T20 Build/RP1A.201005.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/93.0.4577.62 Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "T20", + "type": "tablet" + } + }, + { + "desc": "Nokia T21", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Nokia T21 Build/TP1A.220624.014)", + "expect": { + "vendor": "Nokia", + "model": "T21", + "type": "tablet" + } + }, + { + "desc": "Nokia 2720 Flip", + "ua": "Mozilla/5.0 (Mobile; Nokia_2720_Flip; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5.2", + "expect": { + "vendor": "Nokia", + "model": "2720 Flip", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/nothing.json b/test/specs/devices/nothing.json new file mode 100644 index 000000000..56dc460e3 --- /dev/null +++ b/test/specs/devices/nothing.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Nothing 1", + "ua": "Mozilla/5.0 (Linux; Android 13; A063) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A063", + "type": "mobile" + } + }, + { + "desc": "Nothing 2", + "ua": "Mozilla/5.0 (Linux; Android 14; A065 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.53 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A065", + "type": "mobile" + } + }, + { + "desc": "Nothing 2a", + "ua": "Mozilla/5.0 (Linux; Android 14; A142 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.71 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A142", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/nvidia.json b/test/specs/devices/nvidia.json new file mode 100644 index 000000000..a78316e06 --- /dev/null +++ b/test/specs/devices/nvidia.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Nvidia Shield Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SHIELD Tablet Build/LVY48E; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/45.0.2454.19 Safari/537.36", + "expect": { + "vendor": "Nvidia", + "model": "SHIELD Tablet", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/oneplus.json b/test/specs/devices/oneplus.json new file mode 100644 index 000000000..44407d4e3 --- /dev/null +++ b/test/specs/devices/oneplus.json @@ -0,0 +1,119 @@ +[ + { + "desc": "OnePlus One", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; A0001 Build/KTU84Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.59 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A0001", + "type": "mobile" + } + }, + { + "desc": "OnePlus One", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; OnePlus One A0001 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A0001", + "type": "mobile" + } + }, + { + "desc": "OnePlus 2", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; ONE A2003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A2003", + "type": "mobile" + } + }, + { + "desc": "OnePlus 3", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ONEPLUS A3000 Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A3000", + "type": "mobile" + } + }, + { + "desc": "OnePlus 6", + "ua": "Mozilla/5.0 (Linux; Android 9; ONEPLUS A6003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A6003", + "type": "mobile" + } + }, + { + "desc": "OnePlus 6T", + "ua": "Mozilla/5.0 (Linux; Android 9; ONEPLUS A6010) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A6010", + "type": "mobile" + } + }, + { + "desc": "OnePlus 8T", + "ua": "Mozilla/5.0 (Linux; Android 11; KB2005) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "KB2005", + "type": "mobile" + } + }, + { + "desc": "OnePlus 8 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; IN2025) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.119 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "IN2025", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord N100", + "ua": "Mozilla/5.0 (Linux; Android 10; BE2015 Build/QKQ1.200719.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "BE2015", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord N10 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; BE2029) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.185 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "BE2029", + "type": "mobile" + } + }, + { + "desc": "OnePlus Pad Go 11.35", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; OPD2304) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.629 YaApp_Android/24.101/apad YaSearchBrowser/24.101/apad BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2304", + "type": "tablet" + } + }, + { + "desc": "OnePlus Pad 2 12.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; OPD2403 Build/UKQ1.231108.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2403", + "type": "tablet" + } + }, + { + "desc": "OnePlus Pad 11.61 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; OPD2203 Build/UKQ1.230924.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2203", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/oppo.json b/test/specs/devices/oppo.json new file mode 100644 index 000000000..250909be5 --- /dev/null +++ b/test/specs/devices/oppo.json @@ -0,0 +1,137 @@ +[ + { + "desc": "OnePlus 10RT", + "ua": "Mozilla/5.0 (Linux; Android 13; CPH2413) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH2413", + "type": "mobile" + } + }, + { + "desc": "OPPO Pad", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; OPD2101 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OPD2101", + "type": "tablet" + } + }, + { + "desc": "OPPO Neo", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; R831T Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 OppoBrowser/3.3.2 Mobile Safari/534.30", + "expect": { + "vendor": "OPPO", + "model": "R831T", + "type": "mobile" + } + }, + { + "desc": "OPPO R7s", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; OPPO R7s Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/7.1 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "R7s", + "type": "mobile" + } + }, + { + "desc": "OPPO A3s", + "ua": "Mozilla/5.0 (Linux; Android 8.1; CPH1803 Build/OPM1.171019.026; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH1803", + "type": "mobile" + } + }, + { + "desc": "OPPO A12", + "ua": "Mozilla/5.0 (Linux; Android 9; CPH2083) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH2083", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno", + "ua": "Mozilla/5.0 (Linux; Android 9; PCAT00 Build/PKQ1.190101.001; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PCAT00", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno3 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; PCLM50) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.117 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PCLM50", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno4 SE", + "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; PEAM00 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PEAM00", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno4 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; PDPM00 Build/QKQ1.200216.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PDPM00", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno4 Pro 5G", + "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; PDNT00 Build/QKQ1.200216.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PDNT00", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno5 A", + "ua": "Mozilla/5.0 (Linux; Android 11; A101OP) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "A101OP", + "type": "mobile" + } + }, + { + "desc": "OPPO Find X", + "ua": "Mozilla/5.0 (Linux; Android 8.1; PAFM00 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PAFM00", + "type": "mobile" + } + }, + { + "desc": "OPPO Find 7a", + "ua": "Mozilla/5.0 (Linux; U; Android 4.3; xx-xx; X9007 Build/JLS36C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "OPPO", + "model": "X9007", + "type": "mobile" + } + }, + { + "desc": "OPPO F5", + "ua": "ozilla/5.0 (Linux; Android 7.1.1; CPH1723) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH1723", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/ouya.json b/test/specs/devices/ouya.json new file mode 100644 index 000000000..1fa74c0a1 --- /dev/null +++ b/test/specs/devices/ouya.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Ouya", + "ua": "Mozilla/5.0 (Linux; Android 4.1.2; OUYA Console Build/JZO54L-OUYA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.84 Safari/537.36", + "expect": { + "vendor": "OUYA", + "model": "undefined", + "type": "console" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/panasonic.json b/test/specs/devices/panasonic.json new file mode 100644 index 000000000..568c21de2 --- /dev/null +++ b/test/specs/devices/panasonic.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Panasonic T31", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Panasonic T31 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Mobile Safari/537.36 ", + "expect": { + "vendor": "Panasonic", + "model": "T31", + "type": "mobile" + } + }, + { + "desc": "Panasonic TX-32CSW514 SmartTV", + "ua": "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", + "expect": { + "vendor": "Panasonic", + "model": "VIERA 2015", + "type": "smarttv" + } + }, + { + "desc": "Panasonic TX-40FXW724 SmartTV", + "ua": "HbbTV/1.4.1 (+DRM;Panasonic;SmartTV2018mid;3.024;4301-0003 0002-0000;SmartTV2018;)", + "expect": { + "vendor": "Panasonic", + "model": "SmartTV2018mid", + "type": "smarttv" + } + }, + { + "desc": "Panasonic TX-43HXW904 SmartTV", + "ua": "HbbTV/1.5.1 (+DRM;Panasonic;SmartTV2020mid;3.326;4301-0003 0008-0000;com.panasonic.SmartTV2020mid;)", + "expect": { + "vendor": "Panasonic", + "model": "SmartTV2020mid", + "type": "smarttv" + } + }, + { + "desc": "Panasonic DMR-HST130 SAT receiver", + "ua": "HbbTV/1.1.1 (+PVR;Panasonic;DIGA WebKit M8658;3.420;;)", + "expect": { + "vendor": "Panasonic", + "model": "DIGA WebKit M8658", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/pico.json b/test/specs/devices/pico.json new file mode 100644 index 000000000..2f2821232 --- /dev/null +++ b/test/specs/devices/pico.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "vendor": "PICO", + "model": "4", + "type": "xr" + } + }, + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect": { + "vendor": "PICO", + "model": "4", + "type": "xr" + } + }, + { + "desc": "Pico Neo3 Link", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "vendor": "Pico", + "model": "Neo3 Link", + "type": "xr" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/polytron.json b/test/specs/devices/polytron.json new file mode 100644 index 000000000..595c27f68 --- /dev/null +++ b/test/specs/devices/polytron.json @@ -0,0 +1,38 @@ +[ + { + "desc": "Polytron Prime 7 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; POLYTRON_P552 Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/64.0.3282.137 Mobile Safari/537.36 OPR/50.0.2254.149182", + "expect": { + "vendor": "POLYTRON", + "model": "P552", + "type": "mobile" + } + }, + { + "desc": "Polytron Rocket T1", + "ua": "Mozilla/5.0 (Linux; U; Android 5.0; en-US; POLYTRON R2501 Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/13.1.2.1293 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "R2501", + "type": "mobile" + } + }, + { + "desc": "Polytron Rocket T6", + "ua": "Mozilla/5.0 (Linux; Android 7.0; POLYTRON R2509) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.92 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "R2509", + "type": "mobile" + } + }, + { + "desc": "Polytron Zap 6 Posh", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1; in-ID; POLYTRON_4G501 Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.0.1163 UCTurbo/1.9.9.900 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "4G501", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/realme.json b/test/specs/devices/realme.json new file mode 100644 index 000000000..c16d4d0ac --- /dev/null +++ b/test/specs/devices/realme.json @@ -0,0 +1,74 @@ +[ + { + "desc": "Realme C1", + "ua": "Mozilla/5.0 (Linux; Android 8.1; RMX1811 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1811", + "type": "mobile" + } + }, + { + "desc": "Realme C2", + "ua": "Mozilla/5.0 (Linux; Android 9; RMX1941) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1941", + "type": "mobile" + } + }, + { + "desc": "Realme Narzo 20", + "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; RMX2193 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX2193", + "type": "mobile" + } + }, + { + "desc": "Realme 2 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; RMX1801) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1801", + "type": "mobile" + } + }, + { + "desc": "Realme 3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; RMX1851) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1851", + "type": "mobile" + } + }, + { + "desc": "Realme 8", + "ua": "Mozilla/5.0 (Linux; Android 12; RMX3085) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX3085", + "type": "mobile" + } + }, + { + "desc": "Realme 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; RMX3471) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX3471", + "type": "mobile" + } + }, + { + "desc": "Realme GT Master", + "ua": "Mozilla/5.0 (Linux; Android 13; RMX3363) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX3363", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/roku.json b/test/specs/devices/roku.json new file mode 100644 index 000000000..a042f29b3 --- /dev/null +++ b/test/specs/devices/roku.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Roku", + "ua": "Mozilla/5.0 (Roku) AppleWebKit/537.36 (KHTML, like Gecko) Web/1.1 Safari/537.36", + "expect": { + "vendor": "Roku", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Roku", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 Roku/DVP-8.10 (468.10E04145A)", + "expect": { + "vendor": "Roku", + "model": "DVP-8.10", + "type": "smarttv" + } + }, + { + "desc": "Roku", + "ua": "Roku4640X/DVP-7.70 (297.70E04154A)", + "expect": { + "vendor": "Roku", + "model": "DVP-7.70", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/samsung.json b/test/specs/devices/samsung.json new file mode 100644 index 000000000..0edb0f0b2 --- /dev/null +++ b/test/specs/devices/samsung.json @@ -0,0 +1,371 @@ +[ + { + "desc": "Samsung Galaxy A21s", + "ua": "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-A217F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.0 Chrome/75.0.3770.143 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A217F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A31", + "ua": "Mozilla/5.0 (Linux; Android 10; SM-A315G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A315G", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A50", + "ua": "Mozilla/5.0 (Linux; Android 9; SM-A505F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.105 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A505F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A50s", + "ua": "Mozilla/5.0 (Linux; Android 11; SM-A507FN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A507FN", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A52s", + "ua": "Mozilla/5.0 (Linux; Android 13; SM-A528B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A528B", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A80", + "ua": "Mozilla/5.0 (Linux; Android 9; SM-A805F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.112 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A805F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Fold", + "ua": "Mozilla/5.0 (Linux; Android 9; SAMSUNG SM-F900U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/9.2 Chrome/67.0.3396.87 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-F900U", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Z Flip", + "ua": "Mozilla/5.0 (Linux; Android 10; SM-F700N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-F700N", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Z Fold2", + "ua": "Mozilla/5.0 (Linux; Android 10; SM-F916B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-F916B", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S10E", + "ua": "Mozilla/5.0 (Linux; Android 9; SM-G970F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G970F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S20 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; SCG01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SCG01", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Note 10+", + "ua": "Mozilla/5.0 (Linux; Android 9; SM-N976V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-N976V", + "type": "mobile" + } + }, + { + "desc": "Samsung SM-C5000", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; SM-C5000 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/51.0.2704.81 Mobile Safari/537.36 wkbrowser 4.1.35 3065", + "expect": { + "vendor": "Samsung", + "model": "SM-C5000", + "type": "mobile" + } + }, + { + "desc": "Samsung C8", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; SM-C7108) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-C7108", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Note 8", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; GT-N5100 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "GT-N5100", + "type": "tablet" + } + }, + { + "desc": "Samsung SM-T231", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T231 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T231", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab 6 Lite", + "ua": "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-P610) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/12.0 Chrome/79.0.3945.136 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-P610", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab A 9.7", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; SM-P550 Build/NMF26X; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.90 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-P550", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab A 10.1", + "ua": " Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-T515) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/13.0 Chrome/83.0.4103.106 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T515", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab S7", + "ua": "Mozilla/5.0 (Linux; Android 10; SM-T870) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T870", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab S8", + "ua": "Mozilla/5.0 (Linux; Android 12; SM-X706B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-X706B", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab S", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T700 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T700", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab Pro 10.1", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T520 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T520", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Watch", + "ua": "Mozilla/5.0 (Linux; Tizen 5.5; SAMSUNG SM-R805W) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.0 Chrome/69.0.3497.106 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R805W", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch Active 2", + "ua": "Mozilla/5.0 (Linux; Tizen 5.5; SAMSUNG SM-R820) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.0 Chrome/69.0.3497.106 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R820", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch4", + "ua": "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-R875U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.2. Chrome/102.0.5005.125 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R875U", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-R925U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.2. Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R925U", + "type": "wearable" + } + }, + { + "desc": "Samsung Note 10.1", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-P605) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-P605", + "type": "tablet" + } + }, + { + "desc": "Samsung SmartTV2011", + "ua": "HbbTV/1.1.1 (;;;;;) Maple;2011", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2011", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV2012", + "ua": "HbbTV/1.1.1 (;Samsung;SmartTV2012;;;) WebKit", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2012", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV2014", + "ua": "HbbTV/1.1.1 (;Samsung;SmartTV2014;T-NT14UDEUC-1060.4;;) WebKit", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2014", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV", + "ua": "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", + "expect": { + "vendor": "Samsung", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV HBBTV", + "ua": "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2021:UAU7000", + "type": "smarttv" + } + }, + { + "desc": "Galaxy Nexus", + "ua": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19", + "expect": { + "vendor": "Samsung", + "model": "Galaxy Nexus", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy C9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0; SAMSUNG SM-C900F Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.2 Chrome/44.0.2403.133 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-C900F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S5", + "ua": "Mozilla/5.0 (Linux; Android 5.0; SM-G900F Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.78 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G900F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy J7 Prime", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; SM-G610F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G610F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S6", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-G920I Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G920I", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S6 Edge", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-G925I Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G925I", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Note 5 Chrome", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-N920C Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-N920C", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Note 5 Samsung Browser", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SAMSUNG SM-N920C Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-N920C", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/sharp.json b/test/specs/devices/sharp.json new file mode 100644 index 000000000..32d53409f --- /dev/null +++ b/test/specs/devices/sharp.json @@ -0,0 +1,56 @@ +[ + { + "desc": "Sharp AQUOS-TVX19B", + "ua": "Mozilla/5.0 (Linux; Android 9; AQUOS-TVX19B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "AQUOS-TVX19B", + "type": "smarttv" + } + }, + { + "desc": "Sharp Aquos B10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-A01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-A01", + "type": "mobile" + } + }, + { + "desc": "Sharp Aquos L2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-L02 Build/S4045) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-L02", + "type": "mobile" + } + }, + { + "desc": "Sharp Aquos L2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-L02) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-L02", + "type": "mobile" + } + }, + { + "desc": "Sharp Aquos R2", + "ua": "Mozilla/5.0 (Linux; Android 8.0; SHV42) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SHV42", + "type": "mobile" + } + }, + { + "desc": "Docomo SH-02M", + "ua": "Mozilla/5.0 (Linux; Android 9; SH-02M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-02M", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/smartfren.json b/test/specs/devices/smartfren.json new file mode 100644 index 000000000..5413bb70b --- /dev/null +++ b/test/specs/devices/smartfren.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Smartfren Andromax L", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Andromax B26D2H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Smartfren", + "model": "Andromax B26D2H", + "type": "mobile" + } + }, + { + "desc": "Smartfren Andromax G2", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Smartfren Andromax AD9A1H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.83 Mobile Safari/537.36", + "expect": { + "vendor": "Smartfren", + "model": "Andromax AD9A1H", + "type": "mobile" + } + }, + { + "desc": "Smartfren New Andromax I", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; id-id; New Andromax-i Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Smartfren", + "model": "New Andromax-i", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/sony.json b/test/specs/devices/sony.json new file mode 100644 index 000000000..a4debe725 --- /dev/null +++ b/test/specs/devices/sony.json @@ -0,0 +1,209 @@ +[ + { + "desc": "SONY Xperia 1 III", + "ua": "Mozilla/5.0 (Linux; Android 11; A101SO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "A101SO", + "type": "mobile" + } + }, + { + "desc": "Sony G8141 (Xperia XZ1)", + "ua": "Mozilla/5.0 (Linux; Android 9; SO-01K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SO-01K", + "type": "mobile" + } + }, + { + "desc": "Sony G8141 (Xperia XZ Premium)", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; G8141) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "G8141", + "type": "mobile" + } + }, + { + "desc": "Sony C5303 (Xperia SP)", + "ua": "Mozilla/5.0 (Linux; Android 4.3; C5303 Build/12.1.A.1.205) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.93 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "C5303", + "type": "mobile" + } + }, + { + "desc": "Sony SO-02F (Xperia Z1 F)", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; SO-02F Build/14.1.H.2.119) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SO-02F", + "type": "mobile" + } + }, + { + "desc": "Sony D6653 (Xperia Z3)", + "ua": "Mozilla/5.0 (Linux; Android 4.4; D6653 Build/23.0.A.0.376) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "D6653", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia SOL25 (ZL2)", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4; SOL25 Build/17.1.1.C.1.64) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Sony", + "model": "SOL25", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia SP", + "ua": "Mozilla/5.0 (Linux; Android 4.3; C5302 Build/12.1.A.1.201) AppleWebkit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "C5302", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia L4", + "ua": "Mozilla/5.0 (Linux; Android 9; XQ-AD51) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.83 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AD51", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 1ii", + "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AT51) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AT51", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 1ii", + "ua": "Mozilla/5.0 (Linux; Android 10; SOG01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SOG01", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 10ii", + "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AU52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AU52", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AQ52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.185 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AQ52", + "type": "mobile" + } + }, + { + "desc": "Sony SGP521 (Xperia Z2 Tablet)", + "ua": "Mozilla/5.0 (Linux; Android 4.4; SGP521 Build/17.1.A.0.432) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "Xperia Tablet", + "type": "tablet" + } + }, + { + "desc": "Sony Xperia Z2 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SGP561) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "Xperia Tablet", + "type": "tablet" + } + }, + { + "desc": "Sony Tablet S", + "ua": "Mozilla/5.0 (Linux; U; Android 3.1; Sony Tablet S Build/THMAS10000) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13", + "expect": { + "vendor": "Sony", + "model": "Xperia Tablet", + "type": "tablet" + } + }, + { + "desc": "Sony Tablet Z LTE", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1; SonySGP321 Build/10.2.C.0.143) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "Sony", + "model": "Xperia Tablet", + "type": "tablet" + } + }, + { + "desc": "Sony BRAVIA 4K GB ATV3", + "ua": "Mozilla/5.0 (Linux; Andr0id 9; BRAVIA 4K GB ATV3 Build/PTT1.190515.001.S38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 OPR/46.0.2207.0 OMI/4.13.0.180.DIA5.104 Model/Sony-BRAVIA-4K-GB-ATV3", + "expect": { + "vendor": "Sony", + "model": "BRAVIA 4K GB ATV3", + "type": "smarttv" + } + }, + { + "desc": "Sony BRAVIA 4K GB ATV3", + "ua": "Mozilla/5.0 (Linux; Android 9; BRAVIA 4K GB ATV3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "BRAVIA 4K GB ATV3", + "type": "smarttv" + } + }, + { + "desc": "Sony Bravia 4k UR2", + "ua": "Mozilla/5.0 (Linux: Andr0id 9: BRAVIA 4K UR2 Build/PTT1.190515.001.S104) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 OPR/46.0.2207.0 OMI/4.13.5.431.DIA5HBBTV.250 Model/Sony-BRAVIA-4K-UR2", + "expect": { + "vendor": "Sony", + "model": "BRAVIA 4K UR2", + "type": "smarttv" + } + }, + { + "desc": "PlayStation 4", + "ua": "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", + "expect": { + "vendor": "Sony", + "model": "PlayStation 4", + "type": "console" + } + }, + { + "desc": "PlayStation 5", + "ua": "Mozilla/5.0 (Playstation; Playstation 5/1.05) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15", + "expect": { + "vendor": "Sony", + "model": "Playstation 5", + "type": "console" + } + }, + { + "desc": "PlayStation Vita", + "ua": "Mozilla/5.0 (PlayStation Vita 3.52) AppleWebKit/537.73 (KHTML, like Gecko) Silk/3.2", + "expect": { + "vendor": "Sony", + "model": "PlayStation Vita", + "type": "console" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/tcl.json b/test/specs/devices/tcl.json new file mode 100644 index 000000000..1ea07a31c --- /dev/null +++ b/test/specs/devices/tcl.json @@ -0,0 +1,479 @@ +[ + { + "desc": "TCL 10 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T790Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 EdgA/114.0.1823.43", + "expect": { + "vendor": "TCL", + "model": "T790Y", + "type": "mobile" + } + }, + { + "desc": "TCL 10 5G UW", + "ua": "Mozilla/5.0 (Linux; Android 10; T790S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T790S", + "type": "mobile" + } + }, + { + "desc": "TCL 10 Plus", + "ua": "Mozilla/5.0 (Linux; Android 11; T782H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36 OPR/64.3.3282.60839", + "expect": { + "vendor": "TCL", + "model": "T782H", + "type": "mobile" + } + }, + { + "desc": "TCL 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; T799B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T799B", + "type": "mobile" + } + }, + { + "desc": "TCL 10 SE", + "ua": "Mozilla/5.0 (Linux; Android 10; T766H_RU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T766H", + "type": "mobile" + } + }, + { + "desc": "TCL 10 TabMax", + "ua": "Mozilla/5.0 (Linux; Android 11; 9296Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9296Q", + "type": "tablet" + } + }, + { + "desc": "TCL 10 TabMax 4G", + "ua": "Mozilla/5.0 (Linux; Android 10; 9295G_EEA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9295G", + "type": "tablet" + } + }, + { + "desc": "TCL 10 TabMax WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; 9296G_TR) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9296G", + "type": "tablet" + } + }, + { + "desc": "TCL 10L", + "ua": "Mozilla/5.0 (Linux; Android 10; T770B Build/QKQ1.200329.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 GSA/11.41.10.23.arm64", + "expect": { + "vendor": "TCL", + "model": "T770B", + "type": "mobile" + } + }, + { + "desc": "TCL 10L", + "ua": "Mozilla/5.0 (Linux; Android 11; T770H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T770H", + "type": "mobile" + } + }, + { + "desc": "TCL 20 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T781) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T781", + "type": "mobile" + } + }, + { + "desc": "TCL 20 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T810S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36 EdgA/113.0.1774.63", + "expect": { + "vendor": "TCL", + "model": "T810S", + "type": "mobile" + } + }, + { + "desc": "TCL 20 SE", + "ua": "Mozilla/5.0 (Linux; Android 11; T671H Build/RKQ1.201112.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.73 Mobile Safari/537.36 GoogleApp/13.9.7.23.arm64", + "expect": { + "vendor": "TCL", + "model": "T671H", + "type": "mobile" + } + }, + { + "desc": "TCL 20 XE", + "ua": "Mozilla/5.0 (Linux; Android 11; 5087Z) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "5087Z", + "type": "mobile" + } + }, + { + "desc": "TCL 20B", + "ua": "Mozilla/5.0 (Linux; Android 11; 6159K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6159K", + "type": "mobile" + } + }, + { + "desc": "TCL 205", + "ua": "Mozilla/5.0 (Linux; Android 11; 4187D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "4187D", + "type": "mobile" + } + }, + { + "desc": "TCL 20E", + "ua": "Mozilla/5.0 (Linux; Android 11; 6125A) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/18.0 Chrome/99.0.4844.88 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6125A", + "type": "mobile" + } + }, + { + "desc": "TCL 20L", + "ua": "Mozilla/5.0 (Linux; Android 11; T774H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.59 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T774H", + "type": "mobile" + } + }, + { + "desc": "TCL 20L Plus", + "ua": "Mozilla/5.0 (Linux; Android 11; T775H Build/RKQ1.210107.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.61 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T775H", + "type": "mobile" + } + }, + { + "desc": "TCL 20R 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T767H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36 OPR/71.3.3718.67322", + "expect": { + "vendor": "TCL", + "model": "T767H", + "type": "mobile" + } + }, + { + "desc": "TCL 20S", + "ua": "Mozilla/5.0 (Linux; Android 11; T773O) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T773O", + "type": "mobile" + } + }, + { + "desc": "TCL 20Y", + "ua": "Mozilla/5.0 (Linux; Android 11; 6156D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.134 Mobile Safari/537.36 OPR/70.3.3653.66287", + "expect": { + "vendor": "TCL", + "model": "6156D", + "type": "mobile" + } + }, + { + "desc": "TCL 30 V 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T781S Build/RKQ1.210614.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/369.0.0.5.110;]", + "expect": { + "vendor": "TCL", + "model": "T781S", + "type": "mobile" + } + }, + { + "desc": "TCL 30 XE 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T767W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/416.0.0.35.85;]", + "expect": { + "vendor": "TCL", + "model": "T767W", + "type": "mobile" + } + }, + { + "desc": "TCL 305", + "ua": "Mozilla/5.0 (Linux; arm; Android 11; 6102D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.167 YaBrowser/22.7.6.96.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6102D", + "type": "mobile" + } + }, + { + "desc": "TCL 306", + "ua": "Mozilla/5.0 (Linux; Android 12; 6102H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.141 Mobile Safari/537.36[FBAN/EMA;FBLC/it_IT;FBAV/332.0.0.22.108;]", + "expect": { + "vendor": "TCL", + "model": "6102H", + "type": "mobile" + } + }, + { + "desc": "TCL 30", + "ua": "Mozilla/5.0 (Linux; Android 12; T676H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T676H", + "type": "mobile" + } + }, + { + "desc": "TCL 30+", + "ua": "Mozilla/5.0 (Linux; Android 12; T676J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T676J", + "type": "mobile" + } + }, + { + "desc": "TCL 30 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T776H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T776H", + "type": "mobile" + } + }, + { + "desc": "TCL 30 LE", + "ua": "Mozilla/5.0 (Linux; Android 12; 4188V Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/352.0.0.14.108;]", + "expect": { + "vendor": "TCL", + "model": "4188V", + "type": "mobile" + } + }, + { + "desc": "TCL 30 SE", + "ua": "Mozilla/5.0 (Linux; Android 12; 6165H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/396.1.0.28.104;]", + "expect": { + "vendor": "TCL", + "model": "6165H", + "type": "mobile" + } + }, + { + "desc": "TCL 30E", + "ua": "Mozilla/5.0 (Linux; Android 12; 6127I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6127I", + "type": "mobile" + } + }, + { + "desc": "TCL 40 NxtPaper", + "ua": "Mozilla/5.0 (Linux; Android 13; T612B Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.53 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T612B", + "type": "mobile" + } + }, + { + "desc": "TCL A3", + "ua": "Mozilla/5.0 (Linux; Android 11; A509DL Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 GSA/13.18.7.23.arm64", + "expect": { + "vendor": "TCL", + "model": "A509DL", + "type": "mobile" + } + }, + { + "desc": "TCL A30", + "ua": "Mozilla/5.0 (Linux; Android 11; 5102L Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/413.0.0.30.104;]", + "expect": { + "vendor": "TCL", + "model": "5102L", + "type": "mobile" + } + }, + { + "desc": "TCL 40 SE", + "ua": "Mozilla/5.0 (Linux; Android 13; T610K Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T610K", + "type": "mobile" + } + }, + { + "desc": "TCL 40 XE 5G", + "ua": "Mozilla/5.0 (Linux; Android 13; T609DL Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.136 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/466.1.0.57.85;]", + "expect": { + "vendor": "TCL", + "model": "T609DL", + "type": "mobile" + } + }, + { + "desc": "TCL 403", + "ua": "Mozilla/5.0 (Linux; Android 12; T431D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T431D", + "type": "mobile" + } + }, + { + "desc": "TCL 405", + "ua": "Mozilla/5.0 (Linux; Android 12; T506D Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/418.0.0.33.69;]", + "expect": { + "vendor": "TCL", + "model": "T506D", + "type": "mobile" + } + }, + { + "desc": "TCL 408", + "ua": "Mozilla/5.0 (Linux; U; Android 12; T507U Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/105.0.5195.136 Mobile Safari/537.36 OPR/75.0.2254.68857", + "expect": { + "vendor": "TCL", + "model": "T507U", + "type": "mobile" + } + }, + { + "desc": "TCL 40R 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T771K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 EdgA/114.0.1823.37", + "expect": { + "vendor": "TCL", + "model": "T771K", + "type": "mobile" + } + }, + { + "desc": "TCL Ion X", + "ua": "Mozilla/5.0 (Linux; Android 12; T430W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.60 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T430W", + "type": "mobile" + } + }, + { + "desc": "TCL NxtPaper 11", + "ua": "Mozilla/5.0 (Linux; Android 13; 9466X Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.179 Safari/537.36 [FB_IAB/FB4A;FBAV/473.0.0.41.81;]", + "expect": { + "vendor": "TCL", + "model": "9466X", + "type": "tablet" + } + }, + { + "desc": "TCL Stylus 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T779W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.2 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T779W", + "type": "mobile" + } + }, + { + "desc": "TCL Tab 8 4G", + "ua": "Mozilla/5.0 (Linux; Android 10; 9048S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9048S", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 8 LE", + "ua": "Mozilla/5.0 (Linux; Android 12; 9137W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.61 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9137W", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 FHD 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9060G Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9060G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 HD 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9060X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9060X", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 LTE", + "ua": "Mozilla/5.0 (Linux; Android 13; 8196G Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.162 Safari/537.36 [FB_IAB/FB4A;FBAV/471.0.0.35.80;]", + "expect": { + "vendor": "TCL", + "model": "8196G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 13; 8496G Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.61 Safari/537.36 [FB_IAB/FB4A;FBAV/474.0.0.52.74;]", + "expect": { + "vendor": "TCL", + "model": "8496G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10L", + "ua": "Mozilla/5.0 (Linux; Android 11; 8491X_EEA Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.105 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "8491X", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10s 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9080G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9080G", + "type": "tablet" + } + }, + { + "desc": "TCL Xess P17AA", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TCL Xess P17AA Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "Xess P17AA", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/technisat.json b/test/specs/devices/technisat.json new file mode 100644 index 000000000..e9dca81b1 --- /dev/null +++ b/test/specs/devices/technisat.json @@ -0,0 +1,20 @@ +[ + { + "desc": "TechniSAT Digit ISIO S SAT receiver", + "ua": "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50", + "expect": { + "vendor": "TechniSat", + "model": "Digit ISIO S", + "type": "smarttv" + } + }, + { + "desc": "TechniSAT MultyVision SmartTV", + "ua": "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50", + "expect": { + "vendor": "TechniSat", + "model": "MultyVision ISIO", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/tecno.json b/test/specs/devices/tecno.json new file mode 100644 index 000000000..a002fcf69 --- /dev/null +++ b/test/specs/devices/tecno.json @@ -0,0 +1,20 @@ +[ + { + "desc": "Tecno KC8", + "ua": "Mozilla/5.0 (Linux; Android 10; TECNO KC8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "KC8", + "type": "mobile" + } + }, + { + "desc": "Tecno Spark 8C", + "ua": "Mozilla/5.0 (Linux; Android 11; TECNO KG5n) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "KG5n", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/tesla.json b/test/specs/devices/tesla.json new file mode 100644 index 000000000..34faa3552 --- /dev/null +++ b/test/specs/devices/tesla.json @@ -0,0 +1,20 @@ +[ + { + "desc": "Tesla", + "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1", + "expect": { + "vendor": "Tesla", + "model": "undefined", + "type": "embedded" + } + }, + { + "desc": "Tesla", + "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.16.2.1-e99c70fff409", + "expect": { + "vendor": "Tesla", + "model": "undefined", + "type": "embedded" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/ulefone.json b/test/specs/devices/ulefone.json new file mode 100644 index 000000000..c7aff3261 --- /dev/null +++ b/test/specs/devices/ulefone.json @@ -0,0 +1,92 @@ +[ + { + "desc": "Ulefone Armor", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Armor Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 6.0; Armor) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 YaBrowser/20.4.2.101.00 SA/1 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 8 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; Armor 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.192 Mobile Safari/537.36 OPR/74.1.3922.71199", + "expect": { + "vendor": "Ulefone", + "model": "Armor 8 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 12 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; Armor 12 5G Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor 12 5G", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 20WT", + "ua": "Mozilla/5.0 (Linux; Android 12; Armor 20WT) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor 20WT", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor Pad", + "ua": "Mozilla/5.0 (Linux; Android 12; Armor Pad Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/431.0.0.30.108;]", + "expect": { + "vendor": "Ulefone", + "model": "Armor Pad", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor X5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; Armor X5 Pro Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/430.0.0.23.113;]", + "expect": { + "vendor": "Ulefone", + "model": "Armor X5 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 14 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor14 Pro Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.138 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor14 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 18T", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor 18T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor 18T", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 19T", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor 19T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.192 Mobile Safari/537.36 OPR/74.3.3922.71982", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor 19T", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/vivo.json b/test/specs/devices/vivo.json new file mode 100644 index 000000000..cad62f9d5 --- /dev/null +++ b/test/specs/devices/vivo.json @@ -0,0 +1,74 @@ +[ + { + "desc": "Vivo S1 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1920) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1920", + "type": "mobile" + } + }, + { + "desc": "Vivo Y52s", + "ua": "Mozilla/5.0 (Linux; Android 10; V2057A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.10 SP-engine/2.28.0 baiduboxapp/12.10.0.10 (Baidu; P1 10) NABar/1.0", + "expect": { + "vendor": "Vivo", + "model": "V2057A", + "type": "mobile" + } + }, + { + "desc": "Vivo X60", + "ua": "Mozilla/5.0 (Linux; Android 11; V2046A; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/8.8.71.0", + "expect": { + "vendor": "Vivo", + "model": "V2046A", + "type": "mobile" + } + }, + { + "desc": "Vivo Y79A", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; vivo Y79A Build/N2G47H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.0.14.0", + "expect": { + "vendor": "Vivo", + "model": "Y79A", + "type": "mobile" + } + }, + { + "desc": "Vivo Y93", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; vivo 1814) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1814", + "type": "mobile" + } + }, + { + "desc": "Vivo Y97", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; V1813T Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.0.14.0", + "expect": { + "vendor": "Vivo", + "model": "V1813T", + "type": "mobile" + } + }, + { + "desc": "Vivo iQOO Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; V1916A; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.1.10.6", + "expect": { + "vendor": "Vivo", + "model": "V1916A", + "type": "mobile" + } + }, + { + "desc": "Vivo 1906 (Y11)", + "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1906) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1906", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/xiaomi.json b/test/specs/devices/xiaomi.json new file mode 100644 index 000000000..688bd6182 --- /dev/null +++ b/test/specs/devices/xiaomi.json @@ -0,0 +1,524 @@ +[ + { + "desc": "Xiaomi 2201117TG", + "ua": "Mozilla/5.0 (Linux; Android 11; 2201117TG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.98 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "2201117TG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi M2004J19C", + "ua": "Mozilla/5.0 (Linux; Android 11; M2004J19C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.77 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2004J19C", + "type": "mobile" + } + }, + { + "desc": "Xiaomi M2006C3MNG", + "ua": "Mozilla/5.0 (Linux; Android 11; M2006C3MNG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2006C3MNG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi 21061119DG", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; 21061119DG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 YaBrowser/23.3.7.24.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "21061119DG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi 2013023", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; en-US; 2013023 Build/HM2013023) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/10.0.1.512 U3/0.8.0 Mobile Safari/533.1", + "expect": { + "vendor": "Xiaomi", + "model": "2013023", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Hongmi Note 1W", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-CN; HM NOTE 1W Build/JDQ39) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.7.9.439 U3/0.8.0 Mobile Safari/533.1", + "expect": { + "vendor": "Xiaomi", + "model": "HM NOTE 1W", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 3C", + "ua": "Mozilla/5.0 (Linux; U; Android 4.3; zh-CN; MI 3C Build/JLS36C) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.7.9.439 U3/0.8.0 Mobile Safari/533.1", + "expect": { + "vendor": "Xiaomi", + "model": "MI 3C", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 5", + "ua": "Mozilla/5.0 (Linux; Android 7.0; MI 5 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI 5", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 6", + "ua": "Mozilla/5.0 (Linux; Android 7.1; MI 6 Build/NMF26X; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/59.0.3071.125 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI 6", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 10 Pro", + "ua": "Linux; U; Android 13; Mi 10 Pro Build/TKQ1.221114.001", + "expect": { + "vendor": "Xiaomi", + "model": "Mi 10 Pro", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 5s Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; MI 5s Plus Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/8.7.1", + "expect": { + "vendor": "Xiaomi", + "model": "MI 5s Plus", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi A1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; Mi A1 Build/OPR1.170623.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi A1", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi Note", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; MI NOTE LTE Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI NOTE LTE", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi One Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; MI-ONE Plus Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Xiaomi", + "model": "MI-ONE Plus", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi Max 3", + "ua": "Mozilla/5.0 (Linux; Android 9; MI MAX 3 Build/PKQ1.181007.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI MAX 3", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi A1", + "ua": "Mozilla/5.0 (Linux; Android 9; Mi A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.101 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi A1", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi A2 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; Mi A2 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi A2 Lite", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 9 SE", + "ua": "Mozilla/5.0 (Linux; Android 9; Mi 9 SE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi 9 SE", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi A2", + "ua": "Mozilla/5.0 (Linux; Android 9; Mi A2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi A2", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi CC9", + "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-cn; MI CC 9 Build/RKQ1.200826.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.116 Mobile Safari/537.36 XiaoMi/MiuiBrowser/15.5.18", + "expect": { + "vendor": "Xiaomi", + "model": "MI CC 9", + "type": "mobile" + } + }, + { + "desc": "Xiaomi MI PAD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.4; en-us; MI PAD Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/9.3.2", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 2", + "ua": "Mozilla/5.0 (Linux; Android 5.1; MI PAD 2 Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Safari/537.36 [FB_IAB/FB4A;FBAV/137.0.0.24.91;]", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 2", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 2", + "ua": "Mozilla/5.0 (Linux; x86_64; Android 5.1; MI PAD 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 YaBrowser/20.11.2.69.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 2", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 3", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.0; MI PAD 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.216 YaBrowser/21.5.6.56.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 3", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 4", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 8.1.0; MI PAD 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 YaBrowser/19.9.1.126.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 4", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 4 PLUS", + "ua": "Mozilla/5.0 (Linux; Android 8.1; MI PAD 4 PLUS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 4 PLUS", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 4 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 8.1; Mi Pad4 Wi-Fi) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Mobile Safari/537.36 EdgA/86.0.622.61", + "expect": { + "vendor": "Xiaomi", + "model": "Mi Pad4 Wi-Fi", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5", + "ua": "Mozilla/5.0 (Linux; Android 13; 21051182G Build/TKQ1.221013.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Xiaomi", + "model": "21051182G", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; M2105K81AC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Safari/537.36 Line/13.15.1/IAB", + "expect": { + "vendor": "Xiaomi", + "model": "M2105K81AC", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; M2105K81C) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2105K81C", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 6 Max 14", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-tw; 2307BRPDCC Build/UKQ1.230804.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.118 Mobile Safari/537.36 Device/yudi Model/2307BRPDCC XiaoMi/MiuiBrowser/14.10.6", + "expect": { + "vendor": "Xiaomi", + "model": "2307BRPDCC", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 6 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 13; en-US; 23046RP50C Build/TKQ1.221114.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.6.2.1316 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "23046RP50C", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Pad 6S Pro 12.4", + "ua": "Mozilla/5.0 (Linux; Android 14; 24018RPACC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "24018RPACC", + "type": "tablet" + } + }, + { + "desc": "Xiaomi POCO X2", + "ua": "Mozilla/5.0 (Linux; Android 10; POCO X2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "POCO X2", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO X3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; M2102J20SI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2102J20SI", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO X3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; M2102J20SG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2102J20SG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO X3 NFC", + "ua": "Mozilla/5.0 (Linux; Android 12; M2007J20CG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2007J20CG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO M2 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; POCO M2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.7.42.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "POCO M2 Pro", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO M3", + "ua": "Mozilla/5.0 (Linux; Android 10; M2010J19CI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2010J19CI", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCOPHONE F1", + "ua": "Mozilla/5.0 (Linux; Android 10; POCOPHONE F1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "POCOPHONE F1", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi 4A", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Redmi 4A Build/MMB29M; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi 4A", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi 10C", + "ua": "Mozilla/5.0 (Linux; Android 12; 220333QAG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "220333QAG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi K30 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi K30 5G", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi K30 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi K30 Pro", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi Note 3", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Redmi Note 3 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.116 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 3", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi Note 9 Pro Max", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 9 Pro Max) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.99 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 9 Pro Max", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 9S", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 9S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 9S", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; M2103K19C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2103K19C", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2101K6P", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; M2101K6G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2101K6G", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 8", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 8", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 12 Turbo", + "ua": "Mozilla/5.0 (Linux; Android 13; 23049RAD8C; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 VivoBrowser/16.7.1.1", + "expect": { + "vendor": "Xiaomi", + "model": "23049RAD8C", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Pad", + "ua": "Mozilla/5.0 (Linux; U; Android 12; id-id; Redmi Pad Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36 XiaoMi/MiuiBrowser/14.1.1-gn", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Pad", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad", + "ua": "Mozilla/5.0 (Linux; Android 14; 22081283G Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Xiaomi", + "model": "22081283G", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; 2405CRPFDG Build/UKQ1.240116.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.97 Safari/537.36 [FB_IAB/FB4A;FBAV/476.0.0.49.74;] FBNV/1", + "expect": { + "vendor": "Xiaomi", + "model": "2405CRPFDG", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad SE", + "ua": "Dalvik/2.1.0 (Linux; U; Android 14; 23073RPBFG Build/UKQ1.231003.002)", + "expect": { + "vendor": "Xiaomi", + "model": "23073RPBFG", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad SE 8.7", + "ua": "Mozilla/5.0 (Linux; Android 14; 24076RP19G Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Mobile Safari/537.36 Line/14.18.1/IAB", + "expect": { + "vendor": "Xiaomi", + "model": "24076RP19G", + "type": "tablet" + } + }, + { + "desc": "Xiaomi TV", + "ua": "Mozilla/5.0 (Linux; Android 10; MiTV-MOOQ0 Build/QTG3.200305.006; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/94.0.4606.61 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MiTV-MOOQ0", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/specs/devices/zte.json b/test/specs/devices/zte.json new file mode 100644 index 000000000..5b43beae4 --- /dev/null +++ b/test/specs/devices/zte.json @@ -0,0 +1,11 @@ +[ + { + "desc": "ZTE Blade A6", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE BLADE A0620 Build/NMF26F; ru-ru) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36 Puffin/9.2.0.50586AP", + "expect": { + "vendor": "ZTE", + "model": "BLADE A0620", + "type": "mobile" + } + } +] \ No newline at end of file From b4bc86ac6d887d64b959bd73a16c7141d57f602b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 21 Nov 2024 23:18:38 +0700 Subject: [PATCH 286/388] Improve device detection: Lenovo --- src/main/ua-parser.js | 6 +- test/specs/devices/lenovo.json | 1054 +++++++++++++++++++++++++++++++- 2 files changed, 1043 insertions(+), 17 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a3b867e62..067a3e367 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -588,8 +588,8 @@ ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ // Lenovo - /(ideatab[-\w ]+)/i, - /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i + /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, + /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ // Nokia @@ -701,7 +701,7 @@ /(hp) ([\w ]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i, // Asus /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia - /(lenovo)[-_ ]?([-\w]+)/i, // Lenovo + /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo /(jolla)/i, // Jolla /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ diff --git a/test/specs/devices/lenovo.json b/test/specs/devices/lenovo.json index 2ffc8863c..526a3b50c 100644 --- a/test/specs/devices/lenovo.json +++ b/test/specs/devices/lenovo.json @@ -1,10 +1,154 @@ -[ +[ { - "desc": "Lenovo Tab 2", - "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36", + "desc": "Lenovo A7", + "ua": "Mozilla/5.0 (Linux; U; Android 9; en-US; Lenovo L19111 Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/13.2.8.1301 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L19111", + "type": "mobile" + } + }, + { + "desc": "Lenovo A8", + "ua": "Mozilla/5.0 (Linux; Android 10; Lenovo L10041) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.73 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L10041", + "type": "mobile" + } + }, + { + "desc": "Lenovo dtab Compact 42A", + "ua": "Mozilla/5.0 (Linux; Android 12; d-42A Build/SKQ1.220201.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.5005.125 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "d-42A", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab A7-50", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Lenovo A3500-HV Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "A3500-HV", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab A2109A", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; ru-ru; A2109A Build/JDQ39; CyanogenMod-10.1) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "A2109A", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab S6000", + "ua": "Mozilla/5.0 (Linux; Android 6.0; S6000 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "S6000", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab S6000", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; IdeaTab S6000-H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 YaBrowser/18.11.1.1011.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "IdeaTab S6000-H", + "type": "tablet" + } + }, + { + "desc": "Lenovo K5 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 9;zh-cn; Lenovo L38041 Build/PKQ1.190127.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.117 MobileLenovoBrowser/9.1.3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L38041", + "type": "mobile" + } + }, + { + "desc": "Lenovo K9", + "ua": "Mozilla/5.0 (Linux; U; Android 8.1.0; en-US; Lenovo L38043 Build/O11019) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 UCBrowser/11.4.8.1012 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L38043", + "type": "mobile" + } + }, + { + "desc": "Lenovo K10 Plus", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo L39051) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.66 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L39051", + "type": "mobile" + } + }, + { + "desc": "Lenovo K12", + "ua": "Mozilla/5.0 (Linux; Android 10; Lenovo XT2081-4 Build/QCZ30.30-Q3-45-17; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/409.0.0.27.106;]", + "expect": { + "vendor": "Lenovo", + "model": "XT2081-4", + "type": "mobile" + } + }, + { + "desc": "Lenovo K12", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Lenovo K12 Build/QOGS30.569-83-18; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.130 Mobile Safari/537.36 OPR/69.0.2254.66073", + "expect": { + "vendor": "Lenovo", + "model": "K12", + "type": "mobile" + } + }, + { + "desc": "Lenovo Legion 2 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; Lenovo L70081 Build/RKQ1.201112.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.58 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L70081", + "type": "mobile" + } + }, + { + "desc": "Lenovo Legion Y90", + "ua": "Mozilla/5.0 (Linux; U; Android 12;en-us; Lenovo L71061/SKQ1.211113.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.132 MobileLenovoBrowser/8.6.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L71061", + "type": "mobile" + } + }, + { + "desc": "Lenovo Legion Y700", + "ua": "Mozilla/5.0 (Linux; U; Android 13;zh-cn; Lenovo TB-9707F Build/TKQ1.221013.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.117 MobileLenovoBrowser/2.1.7 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-9707F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Legion Y700", + "ua": "Mozilla/5.0 (Linux; Android 12; TB320FC) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", "expect": { "vendor": "Lenovo", - "model": "TAB 2 A7", + "model": "TB320FC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Moto Tab", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; TB-X704A Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X704A", "type": "tablet" } }, @@ -17,6 +161,141 @@ "type": "mobile" } }, + { + "desc": "Lenovo S5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo L58041) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L58041", + "type": "mobile" + } + }, + { + "desc": "Lenovo Smart Tab M8", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 10; Lenovo TB-8505XS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 YaBrowser/21.11.7.71.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8505XS", + "type": "tablet" + } + }, + { + "desc": "Lenovo Smart Tab M8", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 10; Lenovo TB-8505FS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.166 YaBrowser/21.8.4.111.00 (beta) SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8505FS", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 2", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A7-30HC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 2 A7", + "ua": "Mozilla/5.0 (Linux; Android 7.0.99; Lenovo TAB 2 A7-30DC Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.141 Safari/537.36 OPR/45.1.2246.125351", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A7-30DC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 2 A10", + "ua": "Mozilla/5.0 (Linux; U; Android 5.0.1; Lenovo TAB 2 A10-70L Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.116 Safari/537.36 OPR/29.0.2254.120398", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A10-70L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 2 A10-30", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; TB2-X30F Build/LenovoTB2-X30F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Mobile Safari/537.36 EdgA/90.0.818.49", + "expect": { + "vendor": "Lenovo", + "model": "TB2-X30F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 7", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Lenovo TB3-730X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB3-730X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 7 Essential", + "ua": "Mozilla/5.0 (Linux; Android 5.1; Lenovo TB3-710I Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/85.0.4183.127 Safari/537.36 GSA/5.4.28.19.arm", + "expect": { + "vendor": "Lenovo", + "model": "TB3-710I", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 7 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; en-US; Lenovo TB-7703X Build/S100) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.2.5.1102 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7703X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 8 Dual", + "ua": "Mozilla/5.0 (Linux; Android 6.0; 602LV Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/93.0.4577.82 Safari/537.36 GSA/12.36.22.23.arm64", + "expect": { + "vendor": "Lenovo", + "model": "602LV", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 8 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; Lenovo TB-8703F Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.8 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8703F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 8 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; Lenovo TB-8703X Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Safari/537.36 OPR/33.0.2254.125672", + "expect": { + "vendor": "Lenovo", + "model": "TB-8703X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 10 Business", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; Lenovo TB3-X70F Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Safari/537.36 OPR/32.0.2254.123747", + "expect": { + "vendor": "Lenovo", + "model": "TB3-X70F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 10 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; Lenovo TB3-X70L Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.185 Safari/537.36 OPR/52.1.2254.54298", + "expect": { + "vendor": "Lenovo", + "model": "TB3-X70L", + "type": "tablet" + } + }, { "desc": "Lenovo Tab 3 Pro", "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo YT3-X90F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", @@ -40,35 +319,782 @@ "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Lenovo TAB 2 A7-30HC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", "expect": { "vendor": "Lenovo", - "model": "TAB 2 A7", + "model": "TAB 2 A7-30HC", "type": "tablet" } }, { - "desc": "Lenovo Tab 7 Essential", - "ua": "Mozilla/5.0 (Linux; Android 7.0; Lenovo TB-7304X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", + "desc": "Lenovo Tab 4 8", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Lenovo TB-8504F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36 OPR/64.2.3282.60128", "expect": { "vendor": "Lenovo", - "model": "TB-7304X", + "model": "TB-8504F", "type": "tablet" } }, { - "desc": "Lenovo Tab M10", - "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X606F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 YaBrowser/20.9.4.99.01 Safari/537.36", + "desc": "Lenovo Tab 4 8", + "ua": "Mozilla/5.0 (Linux; U; Android 8.1.0; Lenovo TB-8504X Build/OPM1.171019.019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36 OPR/52.2.2254.54723", "expect": { "vendor": "Lenovo", - "model": "TB-X606F", + "model": "TB-8504X", "type": "tablet" } }, { - "desc": "Lenovo IdeaTab S6000", - "ua": "Mozilla/5.0 (Linux; Android 4.2.2; IdeaTab S6000-H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 YaBrowser/18.11.1.1011.01 Safari/537.36", + "desc": "Lenovo Tab 4 8 Plus", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 8.1.0; Lenovo TB-8704F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 YaApp_Android/21.21.0/apad YaSearchBrowser/21.21.0/apad BroPP/1.0 SA/3 Mobile Safari/537.36", "expect": { "vendor": "Lenovo", - "model": "IdeaTab S6000-H", + "model": "TB-8704F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8 Plus", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; TB-8704V Build/OPM1.171019.019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/104.0.5112.97 Safari/537.36 [FB_IAB/FB4A;FBAV/380.0.0.29.109;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-8704V", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8 Plus", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 8.1.0; Lenovo TB-8704X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 YaBrowser/19.10.4.187.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8704X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8 REL", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Lenovo TB-8X04F Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.100 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8X04F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 10", + "ua": "Mozilla/5.0 (Linux; U; Android 8.1.0; Lenovo TB-X304L Build/OPM1.171019.026; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.149 Safari/537.36 OPR/47.0.2254.146760", + "expect": { + "vendor": "Lenovo", + "model": "TB-X304L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 10 Plus", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.1.1; Lenovo TB-X704L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 YaBrowser/20.12.4.100.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X704L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 10 Plus", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.1.1; Lenovo TB-X704F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 YaBrowser/20.8.5.97.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X704F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 6", + "ua": "Mozilla/5.0 (Linux; Android 11; A101LV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.61 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "A101LV", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 7", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Lenovo TB-7504X Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/119.0.6045.193 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/436.0.0.35.101;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-7504X", "type": "tablet" } + }, + { + "desc": "Lenovo Tab 7 Essential", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; Lenovo TB-7304I Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Safari/537.36 OPR/54.0.2254.56148", + "expect": { + "vendor": "Lenovo", + "model": "TB-7304I", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 7 Essential", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Lenovo TB-7304X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7304X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 10 10.1", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo TB-X103F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X103F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab E7", + "ua": "Mozilla/5.0 (Linux; arm; Android 8.1.0; Lenovo TB-7104I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 BroPP/1.0 SA/3 Mobile Safari/537.36 YandexSearch/7.52/apad YandexSearchBrowser/7.52", + "expect": { + "vendor": "Lenovo", + "model": "TB-7104I", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab E8", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.0; Lenovo TB-8304F1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 YaBrowser/19.7.4.97.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8304F1", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab K10", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-X6C6X Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X6C6X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab K10", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-X6C6F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.83 Safari/537.36 [FB_IAB/FB4A;FBAV/488.0.0.78.79;IABMV/1;] FBNV/5", + "expect": { + "vendor": "Lenovo", + "model": "TB-X6C6F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab K11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 10; Lenovo TB-J606N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 YaApp_Android/22.31.1/apad YaSearchBrowser/22.31.1/apad BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J606N", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab K11 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-J607Z Build/SKQ1.211103.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.58 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J607Z", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M7", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-7305X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.105 Mobile Safari/537.36 OPR/63.3.3216.58675", + "expect": { + "vendor": "Lenovo", + "model": "TB-7305X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M7", + "ua": "Mozilla/5.0 (Linux; arm; Android 9; Lenovo TB-7305I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 YaApp_Android/20.85.0/apad YaSearchBrowser/20.85.0/apad BroPP/1.0 SA/1 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7305I", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M7 (Gen 3)", + "ua": "Mozilla/5.0 (Linux; Android 11; Lenovo TB-7306F Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-7306F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M7 (Gen 3)", + "ua": "Mozilla/5.0 (Linux; arm; Android 11; Lenovo TB-7306X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 YaBrowser/20.12.5.127.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7306X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8", + "ua": "Mozilla/5.0 (Linux; Android 10; Lenovo TB-8505X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8505X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-8505F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 GSA/10.82.8.21.arm64", + "expect": { + "vendor": "Lenovo", + "model": "TB-8505F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8 (Gen 3)", + "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-TW; Lenovo TB-8506X Build/RP1A.200720.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.4.0.1306 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8506X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8 (Gen 4)", + "ua": "Mozilla/5.0 (Linux; Android 13; TB300FU Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/417.0.0.9.97;]", + "expect": { + "vendor": "Lenovo", + "model": "TB300FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8 (Gen 4) (2024)", + "ua": "Mozilla/5.0 (Linux; Android 13; TB301FU Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.170 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/472.0.0.45.79;]", + "expect": { + "vendor": "Lenovo", + "model": "TB301FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8 FHD", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-8705X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.99 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8705X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; Android 12; TB310FU) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB310FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; 13; TB310XU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB310XU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X606F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 YaBrowser/20.9.4.99.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Android 14; Mobile; Lenovo TB-X505F; rv:131.0) Gecko/131.0 Firefox/131.0", + "expect": { + "vendor": "Lenovo", + "model": "TB-X505F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; Android 14; Lenovo TB-X505L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6554.180 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X505L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-X605F Build/PKQ1.190319.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Safari/537.36 [FB_IAB/FB4A;FBAV/298.0.0.46.116;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-X605F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Lenovo TB-X505X Build/QKQ1.191224.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/81.0.4044.138 Safari/537.36 OPR/52.2.2254.54723", + "expect": { + "vendor": "Lenovo", + "model": "TB-X505X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 (Gen 3) ", + "ua": "Mozilla/5.0 (Linux; Android 12; TB328XU Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.58 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB328XU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 (Gen 3) ", + "ua": "Mozilla/5.0 (Linux; Android 12; TB328FU Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.57 Safari/537.36 [FB_IAB/FB4A;FBAV/418.0.0.33.69;]", + "expect": { + "vendor": "Lenovo", + "model": "TB328FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD", + "ua": "Mozilla/5.0 (Linux; Android 13; Lenovo TB-X605FC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6481.193 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X605FC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-X605LC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X605LC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X605L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 YaBrowser/20.12.0.141.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X605L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; Lenovo TB-X606F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6496.93 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD Plus", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-X606FA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5813.205 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606FA", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD Plus", + "ua": "Mozilla/5.0 (Linux; Android 10; Lenovo TB-X606X Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 HD", + "ua": "Mozilla/5.0 (Linux; U; Android 10; it-it; Lenovo TB-X306F Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36 PHX/6.2", + "expect": { + "vendor": "Lenovo", + "model": "TB-X306F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 HD", + "ua": "Mozilla/5.0 (Linux; U; Android 10; it-it; Lenovo TB-X306F Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36 PHX/6.2", + "expect": { + "vendor": "Lenovo", + "model": "TB-X306F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 Plus (Gen 3)", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Lenovo TB-X306X Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.210 Safari/537.36 OPR/55.1.2254.56965", + "expect": { + "vendor": "Lenovo", + "model": "TB-X306X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M11", + "ua": "Mozilla/5.0 (Linux; Android 14; TB330FU Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.60 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB330FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P10", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X705L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 YaBrowser/21.3.3.160.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X705L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; Lenovo TB-J606L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.2311.135 YaBrowser/21.11.5.121.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J606L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11", + "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-cn; Lenovo TB-J606F Build/RKQ1.210303.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.72 MQQBrowser/12.1 Mobile Safari/537.36 COVC/045830", + "expect": { + "vendor": "Lenovo", + "model": "TB-J606F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Plus", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-J616X Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Safari/537.36 [FB_IAB/FB4A;FBAV/418.0.0.33.69;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-J616X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 (Gen 2)", + "ua": "Mozilla/5.0 (Linux; Android 14; TB350FU Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.46 Safari/537.36 [FB_IAB/FB4A;FBAV/490.0.0.63.82;IABMV/1;]", + "expect": { + "vendor": "Lenovo", + "model": "TB350FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Plus", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-J616F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.9 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J616F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-CN; Lenovo TB-J706F Build/RKQ1.201112.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/5.8.6.223 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J706F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; Lenovo TB-J706L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 EdgA/103.0.1264.71", + "expect": { + "vendor": "Lenovo", + "model": "TB-J706L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Pro (Gen 2)", + "ua": "Mozilla/5.0 (Linux; Android 13; TB132FU Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB132FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P12", + "ua": "Mozilla/5.0 (Linux; Android 14; TB370FU Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.106 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB370FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P12 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; Lenovo TB-Q706F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-Q706F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P12 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; Lenovo TB-Q706Z Build/TKQ1.221013.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-Q706Z", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab QT K11 WiFi", + "ua": "Mozilla/5.0 (Linux; arm; Android 12; Lenovo TB-J6C6F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.114 YaBrowser/22.9.3.82.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J6C6F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab QT K11 Pro WiFi", + "ua": "Mozilla/5.0 (Linux; Android 11; Lenovo TB-J607F Build/RKQ1.201217.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J607F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab V7", + "ua": "Mozilla/5.0 (Linux; U; Android 9; en-US; Lenovo PB-6505M Build/PKQ1) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.9.9.1155 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "PB-6505M", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab V7", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo PB-6505Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 YaBrowser/20.9.3.85.00 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "PB-6505Y", + "type": "tablet" + } + }, + { + "desc": "Lenovo X3 Lite", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo X3 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.88 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "X3 Lite", + "type": "mobile" + } + }, + { + "desc": "Lenovo Yoga Smart Tab", + "ua": "Mozilla/5.0 (Android 11; Mobile; Lenovo YT-X705X; rv:129.0) Gecko/129.0 Firefox/129.0", + "expect": { + "vendor": "Lenovo", + "model": "YT-X705X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Smart Tab", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 10; Lenovo YT-X705F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 YaBrowser/20.2.4.153.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT-X705F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Smart Tab", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo YT-X705L Build/PKQ1.181218.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Safari/537.36 GSA/11.38.8.23.arm64", + "expect": { + "vendor": "Lenovo", + "model": "YT-X705L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; Lenovo YT3-X50L Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.149 Safari/537.36 OPR/46.0.2254.145391", + "expect": { + "vendor": "Lenovo", + "model": "YT3-X50L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; Lenovo YT3-X50L Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.149 Safari/537.36 OPR/46.0.2254.145391", + "expect": { + "vendor": "Lenovo", + "model": "YT3-X50L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo YT3-X50M Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-X50M", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 8", + "ua": "Mozilla/5.0(Linux; U; Android 5.1.1; pt-BR; Lenovo YT3-850F Build/LMY47V) AppleWebKit/537.36(KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-850F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 8", + "ua": "Mozilla/5.0(Linux; U; Android 5.1.1; lv-LV; Lenovo YT3-850L Build/LMY47V) AppleWebKit/537.36(KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-850L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 10", + "ua": "Mozilla/5.0(Linux; U; Android 5.1.1; vi-VN; Lenovo YT3-850M Build/LMY47V) AppleWebKit/537.36(KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-850M", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 Plus", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Lenovo YT-X703L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36 OPR/64.2.3282.60128", + "expect": { + "vendor": "Lenovo", + "model": "YT-X703L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 Plus", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; YT-X703F Build/NJH47F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.141 Safari/537.36 OPR/45.0.2246.125120", + "expect": { + "vendor": "Lenovo", + "model": "YT-X703F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; Lenovo YT-J706X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.42 YaBrowser/24.1.1.42.01 (beta) Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT-J706X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 8", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.2; ru-ru; Lenovo B6000; Android/4.4.2; Release/08.26.2015) AppleWebKit/534.30 (KHTML, like Gecko) Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "B6000", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 8", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Lenovo B6000-H Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 GSA/7.24.32.16.arm", + "expect": { + "vendor": "Lenovo", + "model": "B6000-H", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 8", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; es-us; Lenovo B6000-F/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.2.2 Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "B6000-F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 10", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; ru-ru; Lenovo B8000-F Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "B8000-F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 10", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; ru-ru; Lenovo B8000-H Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 Mobile UCBrowser/3.4.3.532", + "expect": { + "vendor": "Lenovo", + "model": "B8000-H", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 10 HD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-US; Lenovo B8080-H Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.0.0.1088 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "B8080-H", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 10 HD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.3; ru-ru; Lenovo B8080-F/JLS36C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.3 Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "B8080-F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Z6", + "ua": "Mozilla/5.0 (Linux; U; Android 9;zh-cn; Lenovo L78121 Build/PKQ1.190319.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.117 MobileLenovoBrowser/9.1.3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L78121", + "type": "mobile" + } } ] \ No newline at end of file From bcf249da06e150c71074291d8597c6c7ebf11e6c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 22 Nov 2024 20:21:07 +0700 Subject: [PATCH 287/388] Fix #767 #763 - Improve type definition for `headers` --- package.json | 3 +++ src/main/ua-parser.d.ts | 12 ++++++++---- test/mocha-test.js | 8 ++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 5a5397823..aa887d2b9 100755 --- a/package.json +++ b/package.json @@ -221,8 +221,11 @@ "@babel/traverse": "7.23.2", "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", + "@types/node": "^22.9.1", + "@types/node-fetch": "^2.6.12", "jshint": "~2.13.6", "mocha": "~8.2.0", + "node-fetch": "^2.7.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 150d8672f..03be66eea 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -2,6 +2,9 @@ // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman +import type { IncomingHttpHeaders } from 'http'; +import type { Headers as FetchAPIHeaders } from 'node-fetch'; + declare namespace UAParser { interface IData { @@ -50,11 +53,12 @@ declare namespace UAParser { type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os'; type UAParserExt = Partial> | Partial>[]; + type UAParserHeaders = Record | IncomingHttpHeaders | FetchAPIHeaders; - export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record): IResult; - export function UAParser(uastring?: string, headers?: Record): IResult; - export function UAParser(extensions?: UAParserExt, headers?: Record): IResult; - export function UAParser(headers?: Record): IResult; + export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: UAParserHeaders): IResult; + export function UAParser(uastring?: string, headers?: UAParserHeaders): IResult; + export function UAParser(extensions?: UAParserExt, headers?: UAParserHeaders): IResult; + export function UAParser(headers?: UAParserHeaders): IResult; export class UAParser { diff --git a/test/mocha-test.js b/test/mocha-test.js index 9b35330eb..ecd7fe0f6 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -10,6 +10,7 @@ var cpus = require('./specs/cpu-all.json'); var devices = readJsonFiles('test/specs/devices'); var engines = require('./specs/engine-all.json'); var os = require('./specs/os-all.json'); +var { Headers } = require('node-fetch'); function readJsonFiles(dir) { var list = []; @@ -354,6 +355,13 @@ describe('Read user-agent data from req.headers', function () { let engine = UAParser(req.headers).engine; assert.strictEqual(engine.name, "EdgeHTML"); }); + + it('Fetch API\'s Header can be passed directly into headers', () => { + const reqHeaders = new Headers(); + reqHeaders.append('User-Agent', 'Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+'); + const { browser } = UAParser(reqHeaders); + assert.strictEqual(browser.is('Midori'), true); + }); }); describe('Map UA-CH headers', function () { From f5ee90fcf89d06079454b2e9479bd41c9093e714 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 22 Nov 2024 22:29:03 +0700 Subject: [PATCH 288/388] Reorganize test files --- package-lock.json | 72 +++++++++++- test/mocha-test-extension.js | 10 +- test/mocha-test.js | 105 ++++++++---------- test/specs/{ => browser}/browser-all.json | 0 test/specs/{ => cpu}/cpu-all.json | 0 test/specs/{devices => device}/_others.json | 0 test/specs/{devices => device}/acer.json | 0 test/specs/{devices => device}/advan.json | 0 test/specs/{devices => device}/alcatel.json | 0 test/specs/{devices => device}/amazon.json | 0 test/specs/{devices => device}/apple.json | 0 test/specs/{devices => device}/asus.json | 0 .../specs/{devices => device}/blackberry.json | 0 test/specs/{devices => device}/cat.json | 0 test/specs/{devices => device}/energizer.json | 0 test/specs/{devices => device}/facebook.json | 0 test/specs/{devices => device}/fairphone.json | 0 test/specs/{devices => device}/google.json | 0 test/specs/{devices => device}/hmd.json | 0 test/specs/{devices => device}/honor.json | 0 test/specs/{devices => device}/htc.json | 0 test/specs/{devices => device}/huawei.json | 0 test/specs/{devices => device}/imo.json | 0 test/specs/{devices => device}/infinix.json | 0 test/specs/{devices => device}/itel.json | 0 test/specs/{devices => device}/jolla.json | 0 test/specs/{devices => device}/kobo.json | 0 test/specs/{devices => device}/lenovo.json | 0 test/specs/{devices => device}/lg.json | 0 test/specs/{devices => device}/meizu.json | 0 test/specs/{devices => device}/micromax.json | 0 test/specs/{devices => device}/microsoft.json | 0 test/specs/{devices => device}/motorola.json | 0 test/specs/{devices => device}/nintendo.json | 0 test/specs/{devices => device}/nokia.json | 0 test/specs/{devices => device}/nothing.json | 0 test/specs/{devices => device}/nvidia.json | 0 test/specs/{devices => device}/oneplus.json | 0 test/specs/{devices => device}/oppo.json | 0 test/specs/{devices => device}/ouya.json | 0 test/specs/{devices => device}/panasonic.json | 0 test/specs/{devices => device}/pico.json | 0 test/specs/{devices => device}/polytron.json | 0 test/specs/{devices => device}/realme.json | 0 test/specs/{devices => device}/roku.json | 0 test/specs/{devices => device}/samsung.json | 0 test/specs/{devices => device}/sharp.json | 0 test/specs/{devices => device}/smartfren.json | 0 test/specs/{devices => device}/sony.json | 0 test/specs/{devices => device}/tcl.json | 0 test/specs/{devices => device}/technisat.json | 0 test/specs/{devices => device}/tecno.json | 0 test/specs/{devices => device}/tesla.json | 0 test/specs/{devices => device}/ulefone.json | 0 test/specs/{devices => device}/vivo.json | 0 test/specs/{devices => device}/xiaomi.json | 0 test/specs/{devices => device}/zte.json | 0 test/specs/{ => engine}/engine-all.json | 0 .../{browser-clis.json => extension/cli.json} | 0 .../crawler.json} | 0 .../email.json} | 0 .../extra-devices.json} | 0 .../fetcher.json} | 0 .../library.json} | 0 .../mediaplayer.json} | 0 test/specs/{ => os}/os-all.json | 0 66 files changed, 121 insertions(+), 66 deletions(-) rename test/specs/{ => browser}/browser-all.json (100%) rename test/specs/{ => cpu}/cpu-all.json (100%) rename test/specs/{devices => device}/_others.json (100%) rename test/specs/{devices => device}/acer.json (100%) rename test/specs/{devices => device}/advan.json (100%) rename test/specs/{devices => device}/alcatel.json (100%) rename test/specs/{devices => device}/amazon.json (100%) rename test/specs/{devices => device}/apple.json (100%) rename test/specs/{devices => device}/asus.json (100%) rename test/specs/{devices => device}/blackberry.json (100%) rename test/specs/{devices => device}/cat.json (100%) rename test/specs/{devices => device}/energizer.json (100%) rename test/specs/{devices => device}/facebook.json (100%) rename test/specs/{devices => device}/fairphone.json (100%) rename test/specs/{devices => device}/google.json (100%) rename test/specs/{devices => device}/hmd.json (100%) rename test/specs/{devices => device}/honor.json (100%) rename test/specs/{devices => device}/htc.json (100%) rename test/specs/{devices => device}/huawei.json (100%) rename test/specs/{devices => device}/imo.json (100%) rename test/specs/{devices => device}/infinix.json (100%) rename test/specs/{devices => device}/itel.json (100%) rename test/specs/{devices => device}/jolla.json (100%) rename test/specs/{devices => device}/kobo.json (100%) rename test/specs/{devices => device}/lenovo.json (100%) rename test/specs/{devices => device}/lg.json (100%) rename test/specs/{devices => device}/meizu.json (100%) rename test/specs/{devices => device}/micromax.json (100%) rename test/specs/{devices => device}/microsoft.json (100%) rename test/specs/{devices => device}/motorola.json (100%) rename test/specs/{devices => device}/nintendo.json (100%) rename test/specs/{devices => device}/nokia.json (100%) rename test/specs/{devices => device}/nothing.json (100%) rename test/specs/{devices => device}/nvidia.json (100%) rename test/specs/{devices => device}/oneplus.json (100%) rename test/specs/{devices => device}/oppo.json (100%) rename test/specs/{devices => device}/ouya.json (100%) rename test/specs/{devices => device}/panasonic.json (100%) rename test/specs/{devices => device}/pico.json (100%) rename test/specs/{devices => device}/polytron.json (100%) rename test/specs/{devices => device}/realme.json (100%) rename test/specs/{devices => device}/roku.json (100%) rename test/specs/{devices => device}/samsung.json (100%) rename test/specs/{devices => device}/sharp.json (100%) rename test/specs/{devices => device}/smartfren.json (100%) rename test/specs/{devices => device}/sony.json (100%) rename test/specs/{devices => device}/tcl.json (100%) rename test/specs/{devices => device}/technisat.json (100%) rename test/specs/{devices => device}/tecno.json (100%) rename test/specs/{devices => device}/tesla.json (100%) rename test/specs/{devices => device}/ulefone.json (100%) rename test/specs/{devices => device}/vivo.json (100%) rename test/specs/{devices => device}/xiaomi.json (100%) rename test/specs/{devices => device}/zte.json (100%) rename test/specs/{ => engine}/engine-all.json (100%) rename test/specs/{browser-clis.json => extension/cli.json} (100%) rename test/specs/{browser-crawlers.json => extension/crawler.json} (100%) rename test/specs/{browser-emails.json => extension/email.json} (100%) rename test/specs/{extension-device.json => extension/extra-devices.json} (100%) rename test/specs/{browser-fetchers.json => extension/fetcher.json} (100%) rename test/specs/{browser-libraries.json => extension/library.json} (100%) rename test/specs/{extension-mediaplayer.json => extension/mediaplayer.json} (100%) rename test/specs/{ => os}/os-all.json (100%) diff --git a/package-lock.json b/package-lock.json index 56374f456..3e7e54781 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,8 +35,11 @@ "@babel/traverse": "7.23.2", "@jazzer.js/core": "^1.4.0", "@playwright/test": "~1.32.2", + "@types/node": "^22.9.1", + "@types/node-fetch": "^2.6.12", "jshint": "~2.13.6", "mocha": "~8.2.0", + "node-fetch": "^2.7.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", @@ -781,10 +784,23 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.15.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", - "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", - "dev": true + "version": "22.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", + "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.2", @@ -3525,6 +3541,26 @@ "integrity": "sha512-YsjmaKGPDkmhoNKIpkChtCsPVaRE0a274IdERKnuc/E8K1UJdBZ4/mvI006OijlQZHCfpRNOH3dfHQs92se8gg==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", @@ -4576,6 +4612,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -4649,6 +4691,12 @@ "node": ">=0.8.0" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -4710,6 +4758,22 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 246bf35aa..6ba195bd7 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -4,11 +4,11 @@ const parseJS = require('@babel/parser').parse; const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); const { UAParser } = require('../src/main/ua-parser'); -const clis = require('./specs/browser-clis.json'); -const crawlers = require('./specs/browser-crawlers.json'); -const emails = require('./specs/browser-emails.json'); -const fetchers = require('./specs/browser-fetchers.json'); -const libraries = require('./specs/browser-libraries.json'); +const clis = require('./specs/extension/cli.json'); +const crawlers = require('./specs/extension/crawler.json'); +const emails = require('./specs/extension/email.json'); +const fetchers = require('./specs/extension/fetcher.json'); +const libraries = require('./specs/extension/library.json'); const { Bots, CLIs, Crawlers, Emails, Fetchers, Libraries } = require('../src/extensions/ua-parser-extensions'); describe('Extensions', () => { diff --git a/test/mocha-test.js b/test/mocha-test.js index ecd7fe0f6..8f6d5290d 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -5,11 +5,11 @@ var requirejs = require('requirejs'); var parseJS = require('@babel/parser').parse; var traverse = require('@babel/traverse').default; var {UAParser} = require('../src/main/ua-parser'); -var browsers = require('./specs/browser-all.json'); -var cpus = require('./specs/cpu-all.json'); -var devices = readJsonFiles('test/specs/devices'); -var engines = require('./specs/engine-all.json'); -var os = require('./specs/os-all.json'); +var browsers = require('./specs/browser/browser-all.json'); +var cpus = require('./specs/cpu/cpu-all.json'); +var devices = readJsonFiles('test/specs/device'); +var engines = require('./specs/engine/engine-all.json'); +var os = require('./specs/os/os-all.json'); var { Headers } = require('node-fetch'); function readJsonFiles(dir) { @@ -20,39 +20,6 @@ function readJsonFiles(dir) { return list; }; -var parser = new UAParser(); -var methods = [ - { - title : 'getBrowser', - label : 'browser', - list : browsers, - properties : ['name', 'major', 'version', 'type'] - }, - { - title : 'getCPU', - label : 'cpu', - list : cpus, - properties : ['architecture'] - }, - { - title : 'getDevice', - label : 'device', - list : devices, - properties : ['model', 'type', 'vendor'] - }, - { - title : 'getEngine', - label : 'engine', - list : engines, - properties : ['name', 'version'] - }, - { - title : 'getOS', - label : 'os', - list : os, - properties : ['name', 'version'] -}]; - describe('UAParser()', function () { var ua = 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6'; assert.deepEqual(UAParser(ua), new UAParser().setUA(ua).getResult()); @@ -66,26 +33,50 @@ describe('UAParser.setUA method does not throw with undefined ua argument', func assert.doesNotThrow(() => new UAParser().setUA(undefined).getResult()); }); -for (var i in methods) { - describe(methods[i]['title'], function () { - for (var j in methods[i]['list']) { - if (!!methods[i]['list'][j].ua) { - describe('[' + methods[i]['list'][j].desc + ']', function () { - describe('"' + methods[i]['list'][j].ua + '"', function () { - var expect = methods[i]['list'][j].expect; - var result = parser.setUA(methods[i]['list'][j].ua).getResult()[methods[i]['label']]; - - methods[i]['properties'].forEach(function(m) { - it('should return ' + methods[i]['label'] + ' ' + m + ': ' + expect[m], function () { - assert.strictEqual(result[m], expect[m] != 'undefined' ? expect[m] : undefined); - }); +describe('UAParser get*() methods', () => { + [ + { + title : 'getBrowser()', + label : 'browser', + list : browsers + }, + { + title : 'getCPU()', + label : 'cpu', + list : cpus + }, + { + title : 'getDevice()', + label : 'device', + list : devices + }, + { + title : 'getEngine()', + label : 'engine', + list : engines + }, + { + title : 'getOS()', + label : 'os', + list : os + } + ] + .forEach(method => { + describe(`[${method.title}]`, () => { + method.list.forEach(unit => { + describe(`[${unit.desc}]: "${unit.ua}"`, () => { + const actual = UAParser(unit.ua)[method.label]; + Object.entries(unit.expect).forEach(entry => { + const [key, val] = entry; + it(`Should return ${key}: ${val}`, () => { + assert.strictEqual(String(val), String(actual[key])); }); }); }); - } - } + }); + }); }); -} +}); describe('Returns', function () { it('getResult() should returns JSON', function(done) { @@ -192,9 +183,9 @@ describe('Testing regexes', function () { describe('Begin testing', function () { it('all regexes in main file', function () { - regexes.forEach(function (regex) { - describe('Test against `safe-regex` : ' + regex, function () { - it('should be safe from potentially vulnerable regex', function () { + describe('Test against `safe-regex` module', function () { + regexes.forEach(function (regex) { + it(`Should pass \`safe-regex\`: ${regex}`, function () { assert.strictEqual(safe(regex), true); }); }); diff --git a/test/specs/browser-all.json b/test/specs/browser/browser-all.json similarity index 100% rename from test/specs/browser-all.json rename to test/specs/browser/browser-all.json diff --git a/test/specs/cpu-all.json b/test/specs/cpu/cpu-all.json similarity index 100% rename from test/specs/cpu-all.json rename to test/specs/cpu/cpu-all.json diff --git a/test/specs/devices/_others.json b/test/specs/device/_others.json similarity index 100% rename from test/specs/devices/_others.json rename to test/specs/device/_others.json diff --git a/test/specs/devices/acer.json b/test/specs/device/acer.json similarity index 100% rename from test/specs/devices/acer.json rename to test/specs/device/acer.json diff --git a/test/specs/devices/advan.json b/test/specs/device/advan.json similarity index 100% rename from test/specs/devices/advan.json rename to test/specs/device/advan.json diff --git a/test/specs/devices/alcatel.json b/test/specs/device/alcatel.json similarity index 100% rename from test/specs/devices/alcatel.json rename to test/specs/device/alcatel.json diff --git a/test/specs/devices/amazon.json b/test/specs/device/amazon.json similarity index 100% rename from test/specs/devices/amazon.json rename to test/specs/device/amazon.json diff --git a/test/specs/devices/apple.json b/test/specs/device/apple.json similarity index 100% rename from test/specs/devices/apple.json rename to test/specs/device/apple.json diff --git a/test/specs/devices/asus.json b/test/specs/device/asus.json similarity index 100% rename from test/specs/devices/asus.json rename to test/specs/device/asus.json diff --git a/test/specs/devices/blackberry.json b/test/specs/device/blackberry.json similarity index 100% rename from test/specs/devices/blackberry.json rename to test/specs/device/blackberry.json diff --git a/test/specs/devices/cat.json b/test/specs/device/cat.json similarity index 100% rename from test/specs/devices/cat.json rename to test/specs/device/cat.json diff --git a/test/specs/devices/energizer.json b/test/specs/device/energizer.json similarity index 100% rename from test/specs/devices/energizer.json rename to test/specs/device/energizer.json diff --git a/test/specs/devices/facebook.json b/test/specs/device/facebook.json similarity index 100% rename from test/specs/devices/facebook.json rename to test/specs/device/facebook.json diff --git a/test/specs/devices/fairphone.json b/test/specs/device/fairphone.json similarity index 100% rename from test/specs/devices/fairphone.json rename to test/specs/device/fairphone.json diff --git a/test/specs/devices/google.json b/test/specs/device/google.json similarity index 100% rename from test/specs/devices/google.json rename to test/specs/device/google.json diff --git a/test/specs/devices/hmd.json b/test/specs/device/hmd.json similarity index 100% rename from test/specs/devices/hmd.json rename to test/specs/device/hmd.json diff --git a/test/specs/devices/honor.json b/test/specs/device/honor.json similarity index 100% rename from test/specs/devices/honor.json rename to test/specs/device/honor.json diff --git a/test/specs/devices/htc.json b/test/specs/device/htc.json similarity index 100% rename from test/specs/devices/htc.json rename to test/specs/device/htc.json diff --git a/test/specs/devices/huawei.json b/test/specs/device/huawei.json similarity index 100% rename from test/specs/devices/huawei.json rename to test/specs/device/huawei.json diff --git a/test/specs/devices/imo.json b/test/specs/device/imo.json similarity index 100% rename from test/specs/devices/imo.json rename to test/specs/device/imo.json diff --git a/test/specs/devices/infinix.json b/test/specs/device/infinix.json similarity index 100% rename from test/specs/devices/infinix.json rename to test/specs/device/infinix.json diff --git a/test/specs/devices/itel.json b/test/specs/device/itel.json similarity index 100% rename from test/specs/devices/itel.json rename to test/specs/device/itel.json diff --git a/test/specs/devices/jolla.json b/test/specs/device/jolla.json similarity index 100% rename from test/specs/devices/jolla.json rename to test/specs/device/jolla.json diff --git a/test/specs/devices/kobo.json b/test/specs/device/kobo.json similarity index 100% rename from test/specs/devices/kobo.json rename to test/specs/device/kobo.json diff --git a/test/specs/devices/lenovo.json b/test/specs/device/lenovo.json similarity index 100% rename from test/specs/devices/lenovo.json rename to test/specs/device/lenovo.json diff --git a/test/specs/devices/lg.json b/test/specs/device/lg.json similarity index 100% rename from test/specs/devices/lg.json rename to test/specs/device/lg.json diff --git a/test/specs/devices/meizu.json b/test/specs/device/meizu.json similarity index 100% rename from test/specs/devices/meizu.json rename to test/specs/device/meizu.json diff --git a/test/specs/devices/micromax.json b/test/specs/device/micromax.json similarity index 100% rename from test/specs/devices/micromax.json rename to test/specs/device/micromax.json diff --git a/test/specs/devices/microsoft.json b/test/specs/device/microsoft.json similarity index 100% rename from test/specs/devices/microsoft.json rename to test/specs/device/microsoft.json diff --git a/test/specs/devices/motorola.json b/test/specs/device/motorola.json similarity index 100% rename from test/specs/devices/motorola.json rename to test/specs/device/motorola.json diff --git a/test/specs/devices/nintendo.json b/test/specs/device/nintendo.json similarity index 100% rename from test/specs/devices/nintendo.json rename to test/specs/device/nintendo.json diff --git a/test/specs/devices/nokia.json b/test/specs/device/nokia.json similarity index 100% rename from test/specs/devices/nokia.json rename to test/specs/device/nokia.json diff --git a/test/specs/devices/nothing.json b/test/specs/device/nothing.json similarity index 100% rename from test/specs/devices/nothing.json rename to test/specs/device/nothing.json diff --git a/test/specs/devices/nvidia.json b/test/specs/device/nvidia.json similarity index 100% rename from test/specs/devices/nvidia.json rename to test/specs/device/nvidia.json diff --git a/test/specs/devices/oneplus.json b/test/specs/device/oneplus.json similarity index 100% rename from test/specs/devices/oneplus.json rename to test/specs/device/oneplus.json diff --git a/test/specs/devices/oppo.json b/test/specs/device/oppo.json similarity index 100% rename from test/specs/devices/oppo.json rename to test/specs/device/oppo.json diff --git a/test/specs/devices/ouya.json b/test/specs/device/ouya.json similarity index 100% rename from test/specs/devices/ouya.json rename to test/specs/device/ouya.json diff --git a/test/specs/devices/panasonic.json b/test/specs/device/panasonic.json similarity index 100% rename from test/specs/devices/panasonic.json rename to test/specs/device/panasonic.json diff --git a/test/specs/devices/pico.json b/test/specs/device/pico.json similarity index 100% rename from test/specs/devices/pico.json rename to test/specs/device/pico.json diff --git a/test/specs/devices/polytron.json b/test/specs/device/polytron.json similarity index 100% rename from test/specs/devices/polytron.json rename to test/specs/device/polytron.json diff --git a/test/specs/devices/realme.json b/test/specs/device/realme.json similarity index 100% rename from test/specs/devices/realme.json rename to test/specs/device/realme.json diff --git a/test/specs/devices/roku.json b/test/specs/device/roku.json similarity index 100% rename from test/specs/devices/roku.json rename to test/specs/device/roku.json diff --git a/test/specs/devices/samsung.json b/test/specs/device/samsung.json similarity index 100% rename from test/specs/devices/samsung.json rename to test/specs/device/samsung.json diff --git a/test/specs/devices/sharp.json b/test/specs/device/sharp.json similarity index 100% rename from test/specs/devices/sharp.json rename to test/specs/device/sharp.json diff --git a/test/specs/devices/smartfren.json b/test/specs/device/smartfren.json similarity index 100% rename from test/specs/devices/smartfren.json rename to test/specs/device/smartfren.json diff --git a/test/specs/devices/sony.json b/test/specs/device/sony.json similarity index 100% rename from test/specs/devices/sony.json rename to test/specs/device/sony.json diff --git a/test/specs/devices/tcl.json b/test/specs/device/tcl.json similarity index 100% rename from test/specs/devices/tcl.json rename to test/specs/device/tcl.json diff --git a/test/specs/devices/technisat.json b/test/specs/device/technisat.json similarity index 100% rename from test/specs/devices/technisat.json rename to test/specs/device/technisat.json diff --git a/test/specs/devices/tecno.json b/test/specs/device/tecno.json similarity index 100% rename from test/specs/devices/tecno.json rename to test/specs/device/tecno.json diff --git a/test/specs/devices/tesla.json b/test/specs/device/tesla.json similarity index 100% rename from test/specs/devices/tesla.json rename to test/specs/device/tesla.json diff --git a/test/specs/devices/ulefone.json b/test/specs/device/ulefone.json similarity index 100% rename from test/specs/devices/ulefone.json rename to test/specs/device/ulefone.json diff --git a/test/specs/devices/vivo.json b/test/specs/device/vivo.json similarity index 100% rename from test/specs/devices/vivo.json rename to test/specs/device/vivo.json diff --git a/test/specs/devices/xiaomi.json b/test/specs/device/xiaomi.json similarity index 100% rename from test/specs/devices/xiaomi.json rename to test/specs/device/xiaomi.json diff --git a/test/specs/devices/zte.json b/test/specs/device/zte.json similarity index 100% rename from test/specs/devices/zte.json rename to test/specs/device/zte.json diff --git a/test/specs/engine-all.json b/test/specs/engine/engine-all.json similarity index 100% rename from test/specs/engine-all.json rename to test/specs/engine/engine-all.json diff --git a/test/specs/browser-clis.json b/test/specs/extension/cli.json similarity index 100% rename from test/specs/browser-clis.json rename to test/specs/extension/cli.json diff --git a/test/specs/browser-crawlers.json b/test/specs/extension/crawler.json similarity index 100% rename from test/specs/browser-crawlers.json rename to test/specs/extension/crawler.json diff --git a/test/specs/browser-emails.json b/test/specs/extension/email.json similarity index 100% rename from test/specs/browser-emails.json rename to test/specs/extension/email.json diff --git a/test/specs/extension-device.json b/test/specs/extension/extra-devices.json similarity index 100% rename from test/specs/extension-device.json rename to test/specs/extension/extra-devices.json diff --git a/test/specs/browser-fetchers.json b/test/specs/extension/fetcher.json similarity index 100% rename from test/specs/browser-fetchers.json rename to test/specs/extension/fetcher.json diff --git a/test/specs/browser-libraries.json b/test/specs/extension/library.json similarity index 100% rename from test/specs/browser-libraries.json rename to test/specs/extension/library.json diff --git a/test/specs/extension-mediaplayer.json b/test/specs/extension/mediaplayer.json similarity index 100% rename from test/specs/extension-mediaplayer.json rename to test/specs/extension/mediaplayer.json diff --git a/test/specs/os-all.json b/test/specs/os/os-all.json similarity index 100% rename from test/specs/os-all.json rename to test/specs/os/os-all.json From 60cc84d58c3733980c70787629586395b87e0099 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 24 Nov 2024 12:19:21 +0700 Subject: [PATCH 289/388] [sub:extensions] Add new bots: Apache-HttpClient, go-http-client, got, GuzzleHttp, Java-http-client, libwww-perl, lua-resty-http, Needle, OkHttp, node-fetch, PHP-SOAP, PostmanRuntime, superagent --- src/extensions/ua-parser-extensions.js | 7 +- src/extensions/ua-parser-extensions.mjs | 7 +- test/mocha-test-extension.js | 2 +- test/specs/extension/library.json | 130 ++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 5 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 2766271bf..5b62e3e1a 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -375,8 +375,11 @@ const MediaPlayers = Object.freeze({ const Libraries = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy/Java/urllib/requests - [/\b(axios|jsdom|scrapy|java|python-urllib|python-requests)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] + // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent + [ + /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:urllib|requests)|scrapy)\/([\w\.]+)/i, + /(jsdom|(?<=\()java)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, LIBRARY]] ] }); diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 20fbee6d2..33f9b99a7 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -379,8 +379,11 @@ const MediaPlayers = Object.freeze({ const Libraries = Object.freeze({ browser : [ - // Axios/jsdom/Scrapy/Java/urllib/requests - [/\b(axios|jsdom|scrapy|java|python-urllib|python-requests)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, LIBRARY]] + // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent + [ + /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:urllib|requests)|scrapy)\/([\w\.]+)/i, + /(jsdom|(?<=\()java)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, LIBRARY]] ] }); diff --git a/test/mocha-test-extension.js b/test/mocha-test-extension.js index 6ba195bd7..c32c1b824 100644 --- a/test/mocha-test-extension.js +++ b/test/mocha-test-extension.js @@ -22,7 +22,7 @@ describe('Extensions', () => { .forEach((list) => { describe(list[0], () => { list[1].forEach((agent) => { - it(`Can detect ${agent.desc}`, () => { + it(`Can detect ${agent.desc}: "${agent.ua}"`, () => { let browser = UAParser(agent.ua, list[2]).browser; assert.strictEqual(String(browser.name), agent.expect.name); assert.strictEqual(String(browser.version), agent.expect.version); diff --git a/test/specs/extension/library.json b/test/specs/extension/library.json index 1ebdcdc48..f6911055d 100644 --- a/test/specs/extension/library.json +++ b/test/specs/extension/library.json @@ -1,4 +1,14 @@ [ + { + "desc" : "Apache-HttpClient", + "ua" : "Apache-HttpClient/4.5.14 (Java/17.0.12)", + "expect" : + { + "name" : "Apache-HttpClient", + "version" : "4.5.14", + "type" : "library" + } + }, { "desc" : "Axios", "ua" : "axios/1.7.2", @@ -9,6 +19,36 @@ "type" : "library" } }, + { + "desc" : "go-http-client", + "ua" : "go-http-client/1.1", + "expect" : + { + "name" : "go-http-client", + "version" : "1.1", + "type" : "library" + } + }, + { + "desc" : "got", + "ua" : "got/9.6.0 (https://github.com/sindresorhus/got)", + "expect" : + { + "name" : "got", + "version" : "9.6.0", + "type" : "library" + } + }, + { + "desc" : "GuzzleHttp", + "ua" : "GuzzleHttp/6.5.5 curl/7.70.0 PHP/7.4.22", + "expect" : + { + "name" : "GuzzleHttp", + "version" : "6.5.5", + "type" : "library" + } + }, { "desc" : "Java", "ua" : "Java/1.6.0_14", @@ -19,6 +59,16 @@ "type" : "library" } }, + { + "desc" : "Java HTTPClient", + "ua" : "Java-http-client/11.0.6", + "expect" : + { + "name" : "Java-http-client", + "version" : "11.0.6", + "type" : "library" + } + }, { "desc" : "jsdom", "ua" : "Mozilla/5.0 (unknown OS) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0", @@ -29,6 +79,76 @@ "type" : "library" } }, + { + "desc" : "libwww-perl", + "ua" : "libwww-perl/6.05", + "expect" : + { + "name" : "libwww-perl", + "version" : "6.05", + "type" : "library" + } + }, + { + "desc" : "lua-resty-http", + "ua" : "lua-resty-http/0.07 (Lua)", + "expect" : + { + "name" : "lua-resty-http", + "version" : "0.07", + "type" : "library" + } + }, + { + "desc" : "Needle", + "ua" : "Needle/3.2.0 (Node.js v18.14.2; win32 x64)", + "expect" : + { + "name" : "Needle", + "version" : "3.2.0", + "type" : "library" + } + }, + { + "desc" : "OkHttp", + "ua" : "okhttp/3.2.0", + "expect" : + { + "name" : "okhttp", + "version" : "3.2.0", + "type" : "library" + } + }, + { + "desc" : "node-fetch", + "ua" : "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)", + "expect" : + { + "name" : "node-fetch", + "version" : "1.0", + "type" : "library" + } + }, + { + "desc" : "PHP-SOAP", + "ua" : "PHP-SOAP/7.4.33", + "expect" : + { + "name" : "PHP-SOAP", + "version" : "7.4.33", + "type" : "library" + } + }, + { + "desc" : "PostmanRuntime", + "ua" : "PostmanRuntime/7.26.5", + "expect" : + { + "name" : "PostmanRuntime", + "version" : "7.26.5", + "type" : "library" + } + }, { "desc" : "Python urllib", "ua" : "Python-urllib/2.6", @@ -58,5 +178,15 @@ "version" : "1.5.0", "type" : "library" } + }, + { + "desc" : "superagent", + "ua" : "node-superagent/5.0.2", + "expect" : + { + "name" : "node-superagent", + "version" : "5.0.2", + "type" : "library" + } } ] From c37f3863c08932ed3c0e8e9d8c4cb01a470f8c66 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 26 Nov 2024 20:13:07 +0700 Subject: [PATCH 290/388] [sub:extensions] Rearrange mediaplayer regexes --- src/extensions/ua-parser-extensions.js | 122 +++++++------------------ test/specs/extension/mediaplayer.json | 36 +------- 2 files changed, 36 insertions(+), 122 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5b62e3e1a..79dd0b0a9 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -267,104 +267,48 @@ const InApps = Object.freeze({ const MediaPlayers = Object.freeze({ browser : [[ - - /(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia - /(coremedia) v([\w\._]+)/i - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i, - // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC + /(apple(?:coremedia|tv))\/([\w\._]+)/i, // Generic Apple CoreMedia + /(coremedia) v([\w\._]+)/i, + // Ares/Nexplayer/OSSProxy + /(ares|clementine|music player daemon|nexplayer|ossproxy) ([\w\.-]+)/i, + // Aqualung/Lyssna/BSPlayer/Clementine/MPD + // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/GnomeMplayer/MoC // NSPlayer/PSP-InternetRadioPlayer/Videos - /(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD - /(lg player|nexplayer)\s([\d\.]+)/i, - /player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - /(nexplayer)\s([\w\.-]+)/i // Nexplayer + // Nero Home/Nero Scout/Nokia + // QuickTime/RealMedia/RadioApp/RadioClientApplication/ + // SoundTap/Totem/Stagefright/Streamium + // XBMC/gvfs/Xine/XMMS/irapp + /^(aqualung|audacious|audimusicstream|amarok|bass|bsplayer|core|gnomemplayer|gvfs|irapp|lyssna|music on console|nero (?:home|scout)|nokia\d+|nsplayer|psp-internetradioplayer|quicktime|rma|radioapp|radioclientapplication|soundtap|stagefright|streamium|totem|videos|xbmc|xine|xmms)\/([\w\.-]+)/i, + /(lg player|nexplayer) ([\d\.]+)/i, + /player\/(nexplayer|lg player) ([\w\.-]+)/i, // NexPlayer/LG Player + /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i, // Gstreamer + /(htc streaming player) [\w_]+ \/ ([\d\.]+)/i, // HTC Streaming Player + /(lavf)([\d\.]+)/i, // Lavf (FFMPEG) + // MPlayer SVN + /(mplayer)(?: |\/)(?:(?:sherpya-){0,1}svn)(?:-| )(r\d+(?:-\d+[\w\.-]+))/i, + / (songbird)\/([\w\.-]+)/i, // Songbird/Philips-Songbird + /(winamp)(?:3 version|mpeg| ) ([\w\.-]+)/i, // Winamp + /(vlc)(?:\/| media player - version )([\w\.-]+)/i, // VLC Videolan + /^(foobar2000|itunes|smp)\/([\d\.]+)/i, // Foobar2000/iTunes/SMP + /com\.(riseupradioalarm)\/([\d\.]*)/i, // RiseUP Radio Alarm + /(mplayer)(?:\s|\/| unknown-)([\w\.\-]+)/i, // MPlayer + // Windows Media Server + /(windows)\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ home media server/i ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(flrp)\/([\w\.-]+)/i // Flip Player ], [[NAME, 'Flip Player'], VERSION, [TYPE, MEDIAPLAYER]], [ - - /(fstream|nativehost|queryseekspider)/i // FStream/NativeHost/QuerySeekSpider + // MPlayer (no other info)/Media Player Classic/Nero ShowTime + // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) + // inlight radio / YourMuze + /(fstream|media player classic|inlight radio|mplayer|nativehost|nero showtime|ocms-bot|queryseekspider|tapinradio|tunein radio|winamp|yourmuze)/i ], [NAME, [TYPE, MEDIAPLAYER]], [ - /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i - // Gstreamer - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player - /(lavf)([\d\.]+)/i // Lavf (FFMPEG) - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(htc_one_s)\/([\d\.]+)/i, // HTC One S - ], [[NAME, /_/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ - - /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i, - // MPlayer SVN - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer - /(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(mplayer)/i, // MPlayer (no other info) - /(yourmuze)/i, // YourMuze - /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime - ], [NAME, [TYPE, MEDIAPLAYER]], [ - - /(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(nokia\d+)\/([\w\.-]+)/i // Nokia - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(winamp)3 version ([\w\.-]+)/i, // Winamp - /(winamp)\s([\w\.-]+)/i, - /(winamp)mpeg\/([\w\.-]+)/i - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) - // inlight radio - ], [NAME, [TYPE, MEDIAPLAYER]], [ - - /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i - // QuickTime/RealMedia/RadioApp/RadioClientApplication/ - // SoundTap/Totem/Stagefright/Streamium - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(smp)([\d\.]+)/i // SMP - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(vlc) media player - version ([\w\.]+)/i, // VLC Videolan - /(vlc)\/([\w\.-]+)/i, - /(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp - /(foobar2000)\/([\d\.]+)/i, // Foobar2000 - /(itunes)\/([\d\.]+)/i // iTunes - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(wmplayer)\/([\w\.-]+)/i, // Windows Media Player - /(windows-media-player)\/([\w\.-]+)/i - ], [[NAME, /-/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ - - /windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i, - // Windows Media Server - ], [VERSION, [NAME, 'Windows'], [TYPE, MEDIAPLAYER]], [ - - /(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ + /(htc_one_s|windows-media-player|wmplayer)\/([\w\.-]+)/i, // HTC One S / Windows Media Player + ], [[NAME, /[_-]/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ - /(rad.io)\s([\d\.]+)/i, // Rad.io - /(radio.(?:de|at|fr))\s([\d\.]+)/i + /(rad.io|radio.(?:de|at|fr)) ([\d\.]+)/i // Rad.io ], [[NAME, 'rad.io'], VERSION, [TYPE, MEDIAPLAYER]] ] }); diff --git a/test/specs/extension/mediaplayer.json b/test/specs/extension/mediaplayer.json index d40ba04f0..d093048a3 100644 --- a/test/specs/extension/mediaplayer.json +++ b/test/specs/extension/mediaplayer.json @@ -24,9 +24,9 @@ "ua" : "AppleTV/3.0.2 (Macintosh; Intel Mac OS X 10.4.7) AppleWebKit/528.18", "expect" : { - "name" : "WebKit", - "version" : "528.18", - "major" : "528" + "name" : "AppleTV", + "version" : "3.0.2", + "major" : "3" } }, { @@ -109,16 +109,6 @@ "major" : "1" } }, - { - "desc" : "Dalvik", - "ua" : "Dalvik/1.2.0 (Linux; U; Android 2.2.1; GT-S5830L Build/FROYO)", - "expect" : - { - "name" : "Dalvik", - "version" : "1.2.0", - "major" : "1" - } - }, { "desc" : "NexPlayer", "ua" : "E97510d/ Player/NexPlayer 4.0", @@ -189,16 +179,6 @@ "major" : "3" } }, - { - "desc" : "Java", - "ua" : "Java/1.4.1_04", - "expect" : - { - "name" : "Java", - "version" : "1.4.1_04", - "major" : "1" - } - }, { "desc" : "LG Player", "ua" : "LG Player 1.0; Android", @@ -509,16 +489,6 @@ "major" : "3" } }, - { - "desc" : "Wget", - "ua" : "Wget/1.12 (darwin10.7.0)", - "expect" : - { - "name" : "Wget", - "version" : "1.12", - "major" : "1" - } - }, { "desc" : "Winamp", "ua" : "Winamp 2.81", From 20e195de8b1677be52196c90ea0ea241519eb7f4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 26 Nov 2024 20:45:23 +0700 Subject: [PATCH 291/388] Move os test spec into separate files --- src/enums/ua-parser-enums.js | 1 + test/specs/os/aix.json | 11 + test/specs/os/amigaos.json | 11 + test/specs/os/android-x86.json | 11 + test/specs/os/android.json | 20 + test/specs/os/arch.json | 11 + test/specs/os/bada.json | 11 + test/specs/os/beos.json | 11 + test/specs/os/blackberry.json | 20 + test/specs/os/centos.json | 11 + test/specs/os/chrome-os.json | 20 + test/specs/os/chromecast-android.json | 11 + test/specs/os/chromecast-fuchsia.json | 11 + test/specs/os/chromecast-linux.json | 11 + test/specs/os/chromecast-smartspeaker.json | 11 + test/specs/os/contiki.json | 11 + test/specs/os/debian.json | 56 + test/specs/os/deepin.json | 11 + test/specs/os/dragonfly.json | 11 + test/specs/os/elementary-os.json | 11 + test/specs/os/fedora.json | 29 + test/specs/os/firefox-os.json | 29 + test/specs/os/freebsd.json | 11 + test/specs/os/fuchsia.json | 11 + test/specs/os/gentoo.json | 39 + test/specs/os/ghostbsd.json | 11 + test/specs/os/haiku.json | 11 + test/specs/os/harmonyos.json | 11 + test/specs/os/hp-ux.json | 11 + test/specs/os/hurd.json | 11 + test/specs/os/ios.json | 56 + test/specs/os/joli.json | 11 + test/specs/os/kaios.json | 11 + test/specs/os/kubuntu.json | 11 + test/specs/os/linpus.json | 11 + test/specs/os/linspire.json | 11 + test/specs/os/linux.json | 11 + test/specs/os/macos.json | 38 + test/specs/os/maemo.json | 11 + test/specs/os/mandriva.json | 11 + test/specs/os/manjaro.json | 11 + test/specs/os/meego.json | 11 + test/specs/os/minix.json | 11 + test/specs/os/mint.json | 29 + test/specs/os/morphos.json | 11 + test/specs/os/netbsd.json | 11 + test/specs/os/netrange.json | 11 + test/specs/os/nettv.json | 11 + test/specs/os/nintendo.json | 11 + test/specs/os/openbsd.json | 11 + test/specs/os/openharmony.json | 11 + test/specs/os/os-all.json | 1322 -------------------- test/specs/os/os2.json | 11 + test/specs/os/palm.json | 11 + test/specs/os/pclinuxos.json | 11 + test/specs/os/pico.json | 26 + test/specs/os/plan9.json | 11 + test/specs/os/playstation.json | 20 + test/specs/os/qnx.json | 11 + test/specs/os/raspbian.json | 20 + test/specs/os/redhat.json | 38 + test/specs/os/rim-tablet-os.json | 11 + test/specs/os/risc-os.json | 11 + test/specs/os/sabayon.json | 11 + test/specs/os/sailfish.json | 11 + test/specs/os/serenityos.json | 11 + test/specs/os/series40.json | 11 + test/specs/os/slackware.json | 11 + test/specs/os/solaris.json | 20 + test/specs/os/suse.json | 11 + test/specs/os/symbian.json | 20 + test/specs/os/tizen.json | 29 + test/specs/os/ubuntu.json | 20 + test/specs/os/unix.json | 11 + test/specs/os/watchos.json | 29 + test/specs/os/webos.json | 65 + test/specs/os/windows-mobile.json | 20 + test/specs/os/windows-phone.json | 29 + test/specs/os/windows.json | 146 +++ test/specs/os/xbox.json | 47 + test/specs/os/zenwalk.json | 11 + 81 files changed, 1471 insertions(+), 1322 deletions(-) create mode 100644 test/specs/os/aix.json create mode 100644 test/specs/os/amigaos.json create mode 100644 test/specs/os/android-x86.json create mode 100644 test/specs/os/android.json create mode 100644 test/specs/os/arch.json create mode 100644 test/specs/os/bada.json create mode 100644 test/specs/os/beos.json create mode 100644 test/specs/os/blackberry.json create mode 100644 test/specs/os/centos.json create mode 100644 test/specs/os/chrome-os.json create mode 100644 test/specs/os/chromecast-android.json create mode 100644 test/specs/os/chromecast-fuchsia.json create mode 100644 test/specs/os/chromecast-linux.json create mode 100644 test/specs/os/chromecast-smartspeaker.json create mode 100644 test/specs/os/contiki.json create mode 100644 test/specs/os/debian.json create mode 100644 test/specs/os/deepin.json create mode 100644 test/specs/os/dragonfly.json create mode 100644 test/specs/os/elementary-os.json create mode 100644 test/specs/os/fedora.json create mode 100644 test/specs/os/firefox-os.json create mode 100644 test/specs/os/freebsd.json create mode 100644 test/specs/os/fuchsia.json create mode 100644 test/specs/os/gentoo.json create mode 100644 test/specs/os/ghostbsd.json create mode 100644 test/specs/os/haiku.json create mode 100644 test/specs/os/harmonyos.json create mode 100644 test/specs/os/hp-ux.json create mode 100644 test/specs/os/hurd.json create mode 100644 test/specs/os/ios.json create mode 100644 test/specs/os/joli.json create mode 100644 test/specs/os/kaios.json create mode 100644 test/specs/os/kubuntu.json create mode 100644 test/specs/os/linpus.json create mode 100644 test/specs/os/linspire.json create mode 100644 test/specs/os/linux.json create mode 100644 test/specs/os/macos.json create mode 100644 test/specs/os/maemo.json create mode 100644 test/specs/os/mandriva.json create mode 100644 test/specs/os/manjaro.json create mode 100644 test/specs/os/meego.json create mode 100644 test/specs/os/minix.json create mode 100644 test/specs/os/mint.json create mode 100644 test/specs/os/morphos.json create mode 100644 test/specs/os/netbsd.json create mode 100644 test/specs/os/netrange.json create mode 100644 test/specs/os/nettv.json create mode 100644 test/specs/os/nintendo.json create mode 100644 test/specs/os/openbsd.json create mode 100644 test/specs/os/openharmony.json delete mode 100644 test/specs/os/os-all.json create mode 100644 test/specs/os/os2.json create mode 100644 test/specs/os/palm.json create mode 100644 test/specs/os/pclinuxos.json create mode 100644 test/specs/os/pico.json create mode 100644 test/specs/os/plan9.json create mode 100644 test/specs/os/playstation.json create mode 100644 test/specs/os/qnx.json create mode 100644 test/specs/os/raspbian.json create mode 100644 test/specs/os/redhat.json create mode 100644 test/specs/os/rim-tablet-os.json create mode 100644 test/specs/os/risc-os.json create mode 100644 test/specs/os/sabayon.json create mode 100644 test/specs/os/sailfish.json create mode 100644 test/specs/os/serenityos.json create mode 100644 test/specs/os/series40.json create mode 100644 test/specs/os/slackware.json create mode 100644 test/specs/os/solaris.json create mode 100644 test/specs/os/suse.json create mode 100644 test/specs/os/symbian.json create mode 100644 test/specs/os/tizen.json create mode 100644 test/specs/os/ubuntu.json create mode 100644 test/specs/os/unix.json create mode 100644 test/specs/os/watchos.json create mode 100644 test/specs/os/webos.json create mode 100644 test/specs/os/windows-mobile.json create mode 100644 test/specs/os/windows-phone.json create mode 100644 test/specs/os/windows.json create mode 100644 test/specs/os/xbox.json create mode 100644 test/specs/os/zenwalk.json diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 37066fca7..42a1462c6 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -323,6 +323,7 @@ const OS = Object.freeze({ IOS: 'iOS', JOLI: 'Joli', KAIOS: 'KaiOS', + KUBUNTU: 'Kubuntu', LINPUS: 'Linpus', LINSPIRE: 'Linspire', LINUX: 'Linux', diff --git a/test/specs/os/aix.json b/test/specs/os/aix.json new file mode 100644 index 000000000..02ccda875 --- /dev/null +++ b/test/specs/os/aix.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (X11; U; AIX 000138384C00; en-US; rv:1.0.1) Gecko/20030213 Netscape/7.0", + "expect" : + { + "name" : "AIX", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/amigaos.json b/test/specs/os/amigaos.json new file mode 100644 index 000000000..398d9b21b --- /dev/null +++ b/test/specs/os/amigaos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "AmigaOS", + "ua" : "Mozilla/4.0 (compatible; AWEB 3.4 SE; AmigaOS)", + "expect" : + { + "name" : "AmigaOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/android-x86.json b/test/specs/os/android-x86.json new file mode 100644 index 000000000..e83d3ca45 --- /dev/null +++ b/test/specs/os/android-x86.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Android-x86", + "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Generic Android-x86) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 OPR/61.2.3076.56749", + "expect" : + { + "name" : "Android-x86", + "version" : "7.1.2" + } + } +] \ No newline at end of file diff --git a/test/specs/os/android.json b/test/specs/os/android.json new file mode 100644 index 000000000..440fafec2 --- /dev/null +++ b/test/specs/os/android.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Android", + "ua" : "Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; VM670 Build/FRG83G) AppleWebKit/533.1 (KHTML, like Gecko)", + "expect" : + { + "name" : "Android", + "version" : "2.2.2" + } + }, + { + "desc" : "KTB-Nexus 5", + "ua" : "APP-My App/1.0 (Linux; Android 4.2.1; Nexus 5 Build/JOP40D)", + "expect" : + { + "name" : "Android", + "version" : "4.2.1" + } + } +] \ No newline at end of file diff --git a/test/specs/os/arch.json b/test/specs/os/arch.json new file mode 100644 index 000000000..0aad66706 --- /dev/null +++ b/test/specs/os/arch.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Arch", + "ua" : "Uzbl (Webkit 1.1.10) (Arch Linux)", + "expect" : + { + "name" : "Arch", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/bada.json b/test/specs/os/bada.json new file mode 100644 index 000000000..1125641a2 --- /dev/null +++ b/test/specs/os/bada.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Bada", + "ua" : "Mozilla/5.0 (SAMSUNG; SAMSUNG-GT-S5253/S5253DDKC1; U; Bada/1.0; en-us) AppleWebKit/533.1 (KHTML, like Gecko) Dolfin/2.0 Mobile WQVGA SMM-MMS/1.2.0 OPN-B", + "expect" : + { + "name" : "Bada", + "version" : "1.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/beos.json b/test/specs/os/beos.json new file mode 100644 index 000000000..045e54f93 --- /dev/null +++ b/test/specs/os/beos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "BeOS", + "ua" : "Mozilla/5.0 (BeOS; U; BeOS BePC; en-US; rv:1.8.1.8pre) Gecko/20070926 SeaMonkey/1.1.5pre", + "expect" : + { + "name" : "BeOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/blackberry.json b/test/specs/os/blackberry.json new file mode 100644 index 000000000..1865ab6fb --- /dev/null +++ b/test/specs/os/blackberry.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "BlackBerry", + "ua" : "BlackBerry9300/5.0.0.912 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/378", + "expect" : + { + "name" : "BlackBerry", + "version" : "5.0.0.912" + } + }, + { + "desc" : "BlackBerry 10", + "ua" : "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.3+ (KHTML, like Gecko) Version/10.0.9.386 Mobile Safari/537.3+", + "expect" : + { + "name" : "BlackBerry", + "version" : "10" + } + } +] \ No newline at end of file diff --git a/test/specs/os/centos.json b/test/specs/os/centos.json new file mode 100644 index 000000000..4fe25b09a --- /dev/null +++ b/test/specs/os/centos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "CentOS", + "ua" : "Konqueror/15.13 (CentOS Linux 7.4; cs-CZ;)", + "expect" : + { + "name" : "CentOS", + "version" : "7.4" + } + } +] \ No newline at end of file diff --git a/test/specs/os/chrome-os.json b/test/specs/os/chrome-os.json new file mode 100644 index 000000000..69bc8786f --- /dev/null +++ b/test/specs/os/chrome-os.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Chrome OS", + "ua" : "Mozilla/5.0 (X11; CrOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome OS", + "version" : "undefined" + } + }, + { + "desc" : "Chromium OS", + "ua" : "Mozilla/5.0 (X11; CrOS x86_64 10575.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", + "expect" : + { + "name" : "Chrome OS", + "version" : "10575.58.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/chromecast-android.json b/test/specs/os/chromecast-android.json new file mode 100644 index 000000000..c0969dd07 --- /dev/null +++ b/test/specs/os/chromecast-android.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Google Chromecast with Google TV", + "ua" : "Mozilla/5.0 (Linux; Android 12.0; Build/STTL.240206.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.0 Safari/537.36 CrKey/1.56.500000 DeviceType/AndroidTV", + "expect" : + { + "name" : "Chromecast Android", + "version" : "12.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/chromecast-fuchsia.json b/test/specs/os/chromecast-fuchsia.json new file mode 100644 index 000000000..5bfecf832 --- /dev/null +++ b/test/specs/os/chromecast-fuchsia.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Google Chromecast Nest Hub", + "ua" : "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 CrKey/1.56.500000", + "expect" : + { + "name" : "Chromecast Fuchsia", + "version" : "1.56.500000" + } + } +] \ No newline at end of file diff --git a/test/specs/os/chromecast-linux.json b/test/specs/os/chromecast-linux.json new file mode 100644 index 000000000..608234665 --- /dev/null +++ b/test/specs/os/chromecast-linux.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Google Chromecast Legacy Linux-Based", + "ua" : "Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.81 Safari/537.36 CrKey/1.42.183786", + "expect" : + { + "name" : "Chromecast Linux", + "version" : "1.42.183786" + } + } +] \ No newline at end of file diff --git a/test/specs/os/chromecast-smartspeaker.json b/test/specs/os/chromecast-smartspeaker.json new file mode 100644 index 000000000..eb6f15b46 --- /dev/null +++ b/test/specs/os/chromecast-smartspeaker.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Google Chromecast Mini Smart Speaker", + "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/SmartSpeaker", + "expect" : + { + "name" : "Chromecast SmartSpeaker", + "version" : "1.56.500000" + } + } +] \ No newline at end of file diff --git a/test/specs/os/contiki.json b/test/specs/os/contiki.json new file mode 100644 index 000000000..b8b0503c3 --- /dev/null +++ b/test/specs/os/contiki.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Contiki", + "ua" : "Contiki/1.0 (Commodore 64; http://dunkels.com/adam/contiki/)", + "expect" : + { + "name" : "Contiki", + "version" : "1.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/debian.json b/test/specs/os/debian.json new file mode 100644 index 000000000..4ae949a82 --- /dev/null +++ b/test/specs/os/debian.json @@ -0,0 +1,56 @@ +[ + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.7 (like Gecko) (Debian)", + "expect" : + { + "name" : "Debian", + "version" : "undefined" + } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; Debian GNU/Linux 8.1 (jessie)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36", + "expect" : + { + "name" : "Debian", + "version" : "8.1" + } + }, + { + "desc" : "Debian", + "ua" : "ELinks/0.12~pre5-4 (textmode; Debian; Linux 3.2.0-4-amd64 x86_64 192x47-2)", + "expect" : + { + "name" : "Debian", + "version" : "undefined" + } + }, + { + "desc" : "Debian", + "ua" : "w3m/0.5.3+debian-19", + "expect" : + { + "name" : "debian", + "version" : "19" + } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.3) Gecko/2008092814 (Debian-3.0.1-1)", + "expect" : + { + "name" : "Debian", + "version" : "3.0.1-1" + } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.24.4; X11) KHTML/3.5.9 (like Gecko) (Debian package 4:3.5.9.dfsg.1-2+b1)", + "expect" : + { + "name" : "Debian", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/deepin.json b/test/specs/os/deepin.json new file mode 100644 index 000000000..b41e2a3f7 --- /dev/null +++ b/test/specs/os/deepin.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Deepin", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; Deepin 15.5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36 NFSBrowser/5.0.0.1886", + "expect" : + { + "name" : "Deepin", + "version" : "15.5" + } + } +] \ No newline at end of file diff --git a/test/specs/os/dragonfly.json b/test/specs/os/dragonfly.json new file mode 100644 index 000000000..f8f3c5833 --- /dev/null +++ b/test/specs/os/dragonfly.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "DragonFly", + "ua" : "Mozilla/5.0 (X11; U; DragonFly i386; de; rv:1.9.1) Gecko/20090720 Firefox/3.5.1", + "expect" : + { + "name" : "DragonFly", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/elementary-os.json b/test/specs/os/elementary-os.json new file mode 100644 index 000000000..1fd5db7b7 --- /dev/null +++ b/test/specs/os/elementary-os.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "elementary OS", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/604.1 (KHTML, like Gecko) Version/11.0 Safari/604.1 elementary OS/0.4 (Loki) Epiphany/3.18.11", + "expect" : + { + "name" : "elementary OS", + "version" : "0.4" + } + } +] \ No newline at end of file diff --git a/test/specs/os/fedora.json b/test/specs/os/fedora.json new file mode 100644 index 000000000..a7abe1fa4 --- /dev/null +++ b/test/specs/os/fedora.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Fedora", + "ua" : "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0", + "expect" : + { + "name" : "Fedora", + "version" : "undefined" + } + }, + { + "desc" : "Fedora", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:2.0) Gecko/20110404 Fedora/16-dev Firefox/4.0", + "expect" : + { + "name" : "Fedora", + "version" : "16-dev" + } + }, + { + "desc" : "Fedora", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; sk; rv:1.9.0.4) Gecko/2008111217 Fedora/3.0.4-1.fc10 Firefox/3.0.4", + "expect" : + { + "name" : "Fedora", + "version" : "3.0.4-1.fc10" + } + } +] \ No newline at end of file diff --git a/test/specs/os/firefox-os.json b/test/specs/os/firefox-os.json new file mode 100644 index 000000000..faf081a15 --- /dev/null +++ b/test/specs/os/firefox-os.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Firefox OS", + "ua" : "Mozilla/5.0 (Mobile; rv:14.0) Gecko/14.0 Firefox/14.0", + "expect" : + { + "name" : "Firefox OS", + "version" : "14.0" + } + }, + { + "desc" : "Firefox OS on Tablet", + "ua" : "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0", + "expect" : + { + "name" : "Firefox OS", + "version" : "26.0" + } + }, + { + "desc" : "Firefox OS on TV", + "ua" : "Mozilla/5.0 (TV; rv:44.0) Gecko/44.0 Firefox/44.0", + "expect" : + { + "name" : "Firefox OS", + "version" : "44.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/freebsd.json b/test/specs/os/freebsd.json new file mode 100644 index 000000000..74544a0a7 --- /dev/null +++ b/test/specs/os/freebsd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "FreeBSD", + "ua" : "Mozilla/5.0 (X11; U; FreeBSD x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16", + "expect" : + { + "name" : "FreeBSD", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/fuchsia.json b/test/specs/os/fuchsia.json new file mode 100644 index 000000000..0759cba17 --- /dev/null +++ b/test/specs/os/fuchsia.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Fuchsia", + "ua" : "Mozilla/5.0 (X11; Fuchsia x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3557.0 Safari/537.36", + "expect" : + { + "name" : "Fuchsia", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/gentoo.json b/test/specs/os/gentoo.json new file mode 100644 index 000000000..8fa207ba6 --- /dev/null +++ b/test/specs/os/gentoo.json @@ -0,0 +1,39 @@ +[ + + { + "desc" : "Gentoo", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080716 (Gentoo) Galeon/2.0.6", + "expect" : + { + "name" : "Gentoo", + "version" : "undefined" + } + }, + { + "desc" : "Gentoo", + "ua" : "Xombrero (X11; U; Gentoo Linux amd64; en-US) Webkit/2.8.5", + "expect" : + { + "name" : "Gentoo", + "version" : "amd64" + } + }, + { + "desc" : "Gentoo", + "ua" : "Xombrero/1.6.4 (Linux amd64; en; Gentoo)", + "expect" : + { + "name" : "Gentoo", + "version" : "undefined" + } + }, + { + "desc" : "Gentoo", + "ua" : "Links (2.8; Linux 3.17.2-gentoo-x86 i686; GNU C 4.8.2; x)", + "expect" : + { + "name" : "gentoo", + "version" : "x86" + } + } +] \ No newline at end of file diff --git a/test/specs/os/ghostbsd.json b/test/specs/os/ghostbsd.json new file mode 100644 index 000000000..5e36d81f8 --- /dev/null +++ b/test/specs/os/ghostbsd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "GhostBSD", + "ua" : "Mozilla/5.0 (X11; GhostBSD/10.3; x86_64; rv:50.0.1) Gecko/20100101 Firefox/50.0.1", + "expect" : + { + "name" : "GhostBSD", + "version" : "10.3" + } + } +] \ No newline at end of file diff --git a/test/specs/os/haiku.json b/test/specs/os/haiku.json new file mode 100644 index 000000000..7005087ad --- /dev/null +++ b/test/specs/os/haiku.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (Macintosh; Intel Haiku R1 x86) AppleWebKit/602.1.1 (KHTML, like Gecko) WebPositive/1.2 Version/8.0 Safari/602.1.1", + "expect" : + { + "name" : "Haiku", + "version" : "R1" + } + } +] \ No newline at end of file diff --git a/test/specs/os/harmonyos.json b/test/specs/os/harmonyos.json new file mode 100644 index 000000000..1fa13efed --- /dev/null +++ b/test/specs/os/harmonyos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "HarmonyOS", + "ua" : "Mozilla/5.0 (Linux; Android 10; HarmonyOS; YAL-AL10; HMSCore 6.3.0.327; GMSCore 21.48.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.0.3.310 Mobile Safari/537.36", + "expect" : + { + "name" : "HarmonyOS", + "version" : "10" + } + } +] \ No newline at end of file diff --git a/test/specs/os/hp-ux.json b/test/specs/os/hp-ux.json new file mode 100644 index 000000000..841474030 --- /dev/null +++ b/test/specs/os/hp-ux.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "HP-UX", + "ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0", + "expect" : + { + "name" : "HP-UX", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/hurd.json b/test/specs/os/hurd.json new file mode 100644 index 000000000..01707284c --- /dev/null +++ b/test/specs/os/hurd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Hurd", + "ua" : "Mozilla/5.0 (X11; Hurd 0.9 i386; en-US) libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.0 Safari/696.96", + "expect" : + { + "name" : "Hurd", + "version" : "0.9" + } + } +] \ No newline at end of file diff --git a/test/specs/os/ios.json b/test/specs/os/ios.json new file mode 100644 index 000000000..66cff1edf --- /dev/null +++ b/test/specs/os/ios.json @@ -0,0 +1,56 @@ +[ + { + "desc" : "iOS in App", + "ua" : "AppName/version CFNetwork/version Darwin/version", + "expect" : + { + "name" : "iOS", + "version" : "undefined" + } + }, + { + "desc" : "iOS with Chrome", + "ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3", + "expect" : + { + "name" : "iOS", + "version" : "5.1.1" + } + }, + { + "desc" : "iOS with Opera Mini", + "ua" : "Opera/9.80 (iPhone; Opera Mini/7.1.32694/27.1407; U; en) Presto/2.8.119 Version/11.10", + "expect" : + { + "name" : "iOS", + "version" : "undefined" + } + }, + { + "desc": "iOS with FaceBook Mobile App", + "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", + "expect": + { + "name" : "iOS", + "version" : "13.6.1" + } + }, + { + "desc": "iOS with Slack App", + "ua": "com.tinyspeck.chatlyio/23.04.10 (iPhone; iOS 16.4.1; Scale/3.00)", + "expect": + { + "name" : "iOS", + "version" : "16.4.1" + } + }, + { + "desc" : "iOS BE App", + "ua" : "APP-BE Test/1.0 (iPad; Apple; CPU iPhone OS 7_0_2 like Mac OS X)", + "expect" : + { + "name" : "iOS", + "version" : "7.0.2" + } + } +] \ No newline at end of file diff --git a/test/specs/os/joli.json b/test/specs/os/joli.json new file mode 100644 index 000000000..e9815fd8d --- /dev/null +++ b/test/specs/os/joli.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Joli", + "ua" : "Mozilla/5.0 (X11; Jolicloud Linux i686) AppleWebKit/537.6 (KHTML, like Gecko) Joli OS/1.2 Chromium/23.0.1240.0 Chrome/23.0.1240.0 Safari/537.6", + "expect" : + { + "name" : "Joli", + "version" : "1.2" + } + } +] \ No newline at end of file diff --git a/test/specs/os/kaios.json b/test/specs/os/kaios.json new file mode 100644 index 000000000..f59572d59 --- /dev/null +++ b/test/specs/os/kaios.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "KaiOS", + "ua" : "Mozilla/5.0 (Mobile; Nokia_8110_4G; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5", + "expect" : + { + "name" : "KAIOS", + "version" : "2.5" + } + } +] \ No newline at end of file diff --git a/test/specs/os/kubuntu.json b/test/specs/os/kubuntu.json new file mode 100644 index 000000000..26342568f --- /dev/null +++ b/test/specs/os/kubuntu.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Kubuntu", + "ua" : "Mozilla/5.0 (compatible; Konqueror/4.4; Linux 2.6.32-22-generic; X11; en_US) KHTML/4.4.3 (like Gecko) Kubuntu", + "expect" : + { + "name" : "Kubuntu", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/linpus.json b/test/specs/os/linpus.json new file mode 100644 index 000000000..40c81691c --- /dev/null +++ b/test/specs/os/linpus.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Linpus", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b5pre) Gecko/2008032619 Linpus/3.0-0.49", + "expect" : + { + "name" : "Linpus", + "version" : "3.0-0.49" + } + } +] \ No newline at end of file diff --git a/test/specs/os/linspire.json b/test/specs/os/linspire.json new file mode 100644 index 000000000..4d2497b24 --- /dev/null +++ b/test/specs/os/linspire.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Linspire", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060803 Firefox/1.5.0.4 Linspire/1.5.0.4", + "expect" : + { + "name" : "Linspire", + "version" : "1.5.0.4" + } + } +] \ No newline at end of file diff --git a/test/specs/os/linux.json b/test/specs/os/linux.json new file mode 100644 index 000000000..ad86c4e87 --- /dev/null +++ b/test/specs/os/linux.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36", + "expect" : + { + "name" : "Linux", + "version" : "x86_64" + } + } +] \ No newline at end of file diff --git a/test/specs/os/macos.json b/test/specs/os/macos.json new file mode 100644 index 000000000..43f90dba9 --- /dev/null +++ b/test/specs/os/macos.json @@ -0,0 +1,38 @@ +[ + { + "desc" : "Mac OS on PowerPC", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)", + "expect" : + { + "name" : "macOS", + "version" : "undefined" + } + }, + { + "desc" : "Mac OS X on x86, x86_64, or aarch64 using Firefox", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", + "expect" : + { + "name" : "macOS", + "version" : "x.y" + } + }, + { + "desc" : "Mac OS X on PowerPC using Firefox", + "ua" : "Mozilla/5.0 (Macintosh; PPC Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", + "expect" : + { + "name" : "macOS", + "version" : "x.y" + } + }, + { + "desc" : "Mac OS", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36", + "expect" : + { + "name" : "macOS", + "version" : "10.6.8" + } + } +] \ No newline at end of file diff --git a/test/specs/os/maemo.json b/test/specs/os/maemo.json new file mode 100644 index 000000000..92dfe7895 --- /dev/null +++ b/test/specs/os/maemo.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Nokia N900 Linux mobile, on the Fennec browser", + "ua" : "Mozilla/5.0 (Maemo; Linux armv7l; rv:10.0) Gecko/20100101 Firefox/10.0 Fennec/10.0", + "expect" : + { + "name" : "Maemo", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/mandriva.json b/test/specs/os/mandriva.json new file mode 100644 index 000000000..3dbccf782 --- /dev/null +++ b/test/specs/os/mandriva.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Mandriva", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.22) Gecko/20110907 Mandriva Linux/1.9.2.22-0.1mdv2010.2 (2010.2) Firefox/3.6.22", + "expect" : + { + "name" : "Mandriva", + "version" : "1.9.2.22-0.1mdv2010.2" + } + } +] \ No newline at end of file diff --git a/test/specs/os/manjaro.json b/test/specs/os/manjaro.json new file mode 100644 index 000000000..ede015f22 --- /dev/null +++ b/test/specs/os/manjaro.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Manjaro", + "ua" : "Mozilla/5.0 (X11; Manjaro 19.0.2; Arch; x64; rv:84.0) Gecko/20100101 Firefox/84.0", + "expect" : + { + "name" : "Manjaro", + "version" : "19.0.2" + } + } +] \ No newline at end of file diff --git a/test/specs/os/meego.json b/test/specs/os/meego.json new file mode 100644 index 000000000..a506cde7a --- /dev/null +++ b/test/specs/os/meego.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "MeeGo", + "ua" : "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + "expect" : + { + "name" : "MeeGo", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/minix.json b/test/specs/os/minix.json new file mode 100644 index 000000000..d39a5148a --- /dev/null +++ b/test/specs/os/minix.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Minix", + "ua" : "Mozilla/5.0 (X11; Original ; Minix 3.3 ; rv:3.0)", + "expect" : + { + "name" : "Minix", + "version" : "3.3" + } + } +] \ No newline at end of file diff --git a/test/specs/os/mint.json b/test/specs/os/mint.json new file mode 100644 index 000000000..e87641e80 --- /dev/null +++ b/test/specs/os/mint.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Mint", + "ua" : "Opera/9.80 (X11; Linux x86_64; Edition Linux Mint) Presto/2.12.388 Version/12.16", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Opera/9.64 (X11; Linux i686; U; Linux Mint; nb) Presto/2.1.1", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Linux Mint/6 (Felicia) Firefox/3.0.4", + "expect" : + { + "name" : "Mint", + "version" : "6" + } + } +] \ No newline at end of file diff --git a/test/specs/os/morphos.json b/test/specs/os/morphos.json new file mode 100644 index 000000000..f255eeb27 --- /dev/null +++ b/test/specs/os/morphos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "MorphOS", + "ua" : "AmigaVoyager/3.4.4 (MorphOS/PPC native)", + "expect" : + { + "name" : "MorphOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/netbsd.json b/test/specs/os/netbsd.json new file mode 100644 index 000000000..3a7657a27 --- /dev/null +++ b/test/specs/os/netbsd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "NetBSD", + "ua" : "ELinks (0.4.3; NetBSD 3.0.2PATCH sparc64; 141x19)", + "expect" : + { + "name" : "NetBSD", + "version" : "3.0.2PATCH" + } + } +] \ No newline at end of file diff --git a/test/specs/os/netrange.json b/test/specs/os/netrange.json new file mode 100644 index 000000000..a7ff0a988 --- /dev/null +++ b/test/specs/os/netrange.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Netrange Smart TV", + "ua" : "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", + "expect" : + { + "name" : "NETRANGE", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/nettv.json b/test/specs/os/nettv.json new file mode 100644 index 000000000..2584e4096 --- /dev/null +++ b/test/specs/os/nettv.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "NetTV 3.2.1", + "ua" : "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70", + "expect" : + { + "name" : "NETTV", + "version" : "3.2.1" + } + } +] \ No newline at end of file diff --git a/test/specs/os/nintendo.json b/test/specs/os/nintendo.json new file mode 100644 index 000000000..b8ac8a398 --- /dev/null +++ b/test/specs/os/nintendo.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Nintendo Switch", + "ua" : "Mozilla/5.0 (Nintendo Switch; WifiWebAuthApplet) AppleWebKit/606.4 (KHTML, like Gecko) NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393", + "expect" : + { + "name" : "Nintendo", + "version" : "Switch" + } + } +] \ No newline at end of file diff --git a/test/specs/os/openbsd.json b/test/specs/os/openbsd.json new file mode 100644 index 000000000..25164de54 --- /dev/null +++ b/test/specs/os/openbsd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "OpenBSD", + "ua" : "Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.9.1) Gecko/20090702 Firefox/3.5", + "expect" : + { + "name" : "OpenBSD", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/openharmony.json b/test/specs/os/openharmony.json new file mode 100644 index 000000000..b0606a1c4 --- /dev/null +++ b/test/specs/os/openharmony.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "OpenHarmony", + "ua" : "Mozilla/5.0 (Phone; OpenHarmony 4.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile", + "expect" : + { + "name" : "OpenHarmony", + "version" : "4.1" + } + } +] \ No newline at end of file diff --git a/test/specs/os/os-all.json b/test/specs/os/os-all.json deleted file mode 100644 index b93dc33bd..000000000 --- a/test/specs/os/os-all.json +++ /dev/null @@ -1,1322 +0,0 @@ -[ - { - "desc" : "Windows 95", - "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)", - "expect" : - { - "name" : "Windows", - "version" : "95" - } - }, - { - "desc" : "Windows 98", - "ua" : "Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)", - "expect" : - { - "name" : "Windows", - "version" : "98" - } - }, - { - "desc" : "Windows ME", - "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90) Gecko/20020502 CS 2000 7.0/7.0", - "expect" : - { - "name" : "Windows", - "version" : "ME" - } - }, - { - "desc" : "Windows 2000", - "ua" : "Mozilla/3.0 (compatible; MSIE 3.0; Windows NT 5.0)", - "expect" : - { - "name" : "Windows", - "version" : "2000" - } - }, - { - "desc" : "Windows XP", - "ua" : "Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 5.2)", - "expect" : - { - "name" : "Windows", - "version" : "XP" - } - }, - { - "desc" : "Windows Vista", - "ua" : "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; fr-FR)", - "expect" : - { - "name" : "Windows", - "version" : "Vista" - } - }, - { - "desc" : "Windows 7", - "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", - "expect" : - { - "name" : "Windows", - "version" : "7" - } - }, - { - "desc" : "Windows 8", - "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C)", - "expect" : - { - "name" : "Windows", - "version" : "8" - } - }, - { - "desc" : "Windows 10", - "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", - "expect" : - { - "name" : "Windows", - "version" : "10" - } - }, - { - "desc" : "WeChat Desktop for Windows Built-in Browser", - "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", - "expect" : - { - "name" : "Windows", - "version" : "7" - } - }, - { - "desc" : "WeChat Desktop for Windows Built-in Browser major version in 4", - "ua" : "mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/81.0.4044.138 safari/537.36 nettype/wifi micromessenger/7.0.20.1781(0x6700143b) windowswechat", - "expect" : - { - "name" : "Windows", - "version" : "7" - } - }, - { - "desc" : "Windows RT", - "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", - "expect" : - { - "name" : "Windows", - "version" : "RT" - } - }, - { - "desc" : "Windows CE", - "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)", - "expect" : - { - "name" : "Windows", - "version" : "CE" - } - }, - { - "desc" : "Windows Mobile", - "ua" : "Mozilla/5.0 (ZTE-E_N72/N72V1.0.0B02;U;Windows Mobile/6.1;Profile/MIDP-2.0 Configuration/CLDC-1.1;320*240;CTC/2.0) IE/6.0 (compatible; MSIE 4.01; Windows CE; PPC)/UC Browser7.7.1.88", - "expect" : - { - "name" : "Windows Mobile", - "version" : "6.1" - } - }, - { - "desc" : "Windows Mobile", - "ua" : "Opera/9.80 (Windows Mobile; WCE; Opera Mobi/WMD-50433; U; en) Presto/2.4.13 Version/10.00", - "expect" : - { - "name" : "Windows Mobile", - "version" : "undefined" - } - }, - { - "desc" : "Windows Phone", - "ua" : "Opera/9.80 (Windows Phone; Opera Mini/7.6.8/35.7518; U; ru) Presto/2.8.119 Version/11.10", - "expect" : - { - "name" : "Windows Phone", - "version" : "undefined" - } - }, - { - "desc" : "Windows Phone OS", - "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; DELL; Venue Pro)", - "expect" : - { - "name" : "Windows Phone OS", - "version" : "7.0" - } - }, - { - "desc" : "Windows Phone 8", - "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; HTC; Windows Phone 8X by HTC)", - "expect" : - { - "name" : "Windows Phone", - "version" : "8.0" - } - }, - { - "desc" : "Windows NT on x86 or aarch64 CPU using Firefox", - "ua" : "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "Windows", - "version" : "NT x" - } - }, - { - "desc" : "Windows NT on x64 CPU using Firefox", - "ua" : "Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "Windows", - "version" : "NT x" - } - }, - { - "desc" : "BlackBerry", - "ua" : "BlackBerry9300/5.0.0.912 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/378", - "expect" : - { - "name" : "BlackBerry", - "version" : "5.0.0.912" - } - }, - { - "desc" : "BlackBerry 10", - "ua" : "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.3+ (KHTML, like Gecko) Version/10.0.9.386 Mobile Safari/537.3+", - "expect" : - { - "name" : "BlackBerry", - "version" : "10" - } - }, - { - "desc" : "Tizen", - "ua" : "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", - "expect" : - { - "name" : "Tizen", - "version" : "2.3" - } - }, - { - "desc" : "Tizen", - "ua" : "Mozilla/5.0 (Linux; Tizen 2.3; SAMSUNG SM-Z130H) AppleWebKit/537.3 (KHTML, like Gecko) Version/2.3 Mobile Safari/537.3", - "expect" : - { - "name" : "Tizen", - "version" : "2.3" - } - }, - { - "desc" : "Tizen 6.0", - "ua" : "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", - "expect" : - { - "name" : "Tizen", - "version" : "6.0" - } - }, - { - "desc" : "Android", - "ua" : "Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; VM670 Build/FRG83G) AppleWebKit/533.1 (KHTML, like Gecko)", - "expect" : - { - "name" : "Android", - "version" : "2.2.2" - } - }, - { - "desc" : "HarmonyOS", - "ua" : "Mozilla/5.0 (Linux; Android 10; HarmonyOS; YAL-AL10; HMSCore 6.3.0.327; GMSCore 21.48.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.0.3.310 Mobile Safari/537.36", - "expect" : - { - "name" : "HarmonyOS", - "version" : "10" - } - }, - { - "desc" : "Sailfish", - "ua" : "Mozilla/5.0 (Linux; U; Sailfish 3.0; Mobile; rv:45.0) Gecko/45.0 Firefox/45.0 SailfishBrowser/1.0", - "expect" : - { - "name" : "Sailfish", - "version" : "3.0" - } - }, - { - "desc" : "WebOS", - "ua" : "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.5; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.83 Safari/534.6 TouchPad/1.0", - "expect" : - { - "name" : "webOS", - "version" : "3.0.5" - } - }, - { - "desc" : "WebOS", - "ua" : "Mozilla/5.0 (webOS/1.4.5; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.0", - "expect" : - { - "name" : "webOS", - "version" : "1.4.5" - } - }, - { - "desc" : "WebOS TV 5.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 WebAppManager", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "WebOS TV 4.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.34 Safari/537.36 WebAppManager", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "WebOS TV 3.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.2.1 Chrome/38.0.2125.122 Safari/537.36 WebAppManager", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "WebOS TV 2.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/538.2 (KHTML, like Gecko) Large Screen WebAppManager Safari/538.2", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "WebOS TV 1.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen WebAppManager Safari/537.41", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "QNX", - "ua" : "Mozilla/5.0 (Photon; U; QNX x86pc; en-US; rv:1.8.1.20) Gecko/20090127 BonEcho/2.0.0.20", - "expect" : - { - "name" : "QNX", - "version" : "x86pc" - } - }, - { - "desc" : "Bada", - "ua" : "Mozilla/5.0 (SAMSUNG; SAMSUNG-GT-S5253/S5253DDKC1; U; Bada/1.0; en-us) AppleWebKit/533.1 (KHTML, like Gecko) Dolfin/2.0 Mobile WQVGA SMM-MMS/1.2.0 OPN-B", - "expect" : - { - "name" : "Bada", - "version" : "1.0" - } - }, - { - "desc" : "RIM Tablet OS", - "ua" : "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+", - "expect" : - { - "name" : "RIM Tablet OS", - "version" : "2.1.0" - } - }, - { - "desc" : "Nokia N900 Linux mobile, on the Fennec browser", - "ua" : "Mozilla/5.0 (Maemo; Linux armv7l; rv:10.0) Gecko/20100101 Firefox/10.0 Fennec/10.0", - "expect" : - { - "name" : "Maemo", - "version" : "undefined" - } - }, - { - "desc" : "MeeGo", - "ua" : "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", - "expect" : - { - "name" : "MeeGo", - "version" : "undefined" - } - }, - { - "desc" : "Symbian", - "ua" : "Nokia5250/10.0.011 (SymbianOS/9.4; U; Series60/5.0 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/525 (KHTML, like Gecko) Safari/525 3gpp-gba", - "expect" : - { - "name" : "Symbian", - "version" : "9.4" - } - }, - { - "desc" : "Symbian", - "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaC7-00/024.001; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.37 Mobile Safari/533.4 3gpp-gba", - "expect" : - { - "name" : "Symbian", - "version" : "5.2" - } - }, - { - "desc" : "Series40", - "ua" : "Mozilla/5.0 (Series40; Nokia2055/03.20; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/2.2.0.0.34", - "expect" : - { - "name" : "Series40", - "version" : "undefined" - } - }, - { - "desc" : "Firefox OS", - "ua" : "Mozilla/5.0 (Mobile; rv:14.0) Gecko/14.0 Firefox/14.0", - "expect" : - { - "name" : "Firefox OS", - "version" : "14.0" - } - }, - { - "desc" : "Firefox OS on Tablet", - "ua" : "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0", - "expect" : - { - "name" : "Firefox OS", - "version" : "26.0" - } - }, - { - "desc" : "Firefox OS on TV", - "ua" : "Mozilla/5.0 (TV; rv:44.0) Gecko/44.0 Firefox/44.0", - "expect" : - { - "name" : "Firefox OS", - "version" : "44.0" - } - }, - { - "desc" : "Google Chromecast with Google TV", - "ua" : "Mozilla/5.0 (Linux; Android 12.0; Build/STTL.240206.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.0 Safari/537.36 CrKey/1.56.500000 DeviceType/AndroidTV", - "expect" : - { - "name" : "Chromecast Android", - "version" : "12.0" - } - }, - { - "desc" : "Google Chromecast Nest Hub", - "ua" : "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 CrKey/1.56.500000", - "expect" : - { - "name" : "Chromecast Fuchsia", - "version" : "1.56.500000" - } - }, - { - "desc" : "Google Chromecast Mini Smart Speaker", - "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/SmartSpeaker", - "expect" : - { - "name" : "Chromecast SmartSpeaker", - "version" : "1.56.500000" - } - }, - { - "desc" : "Google Chromecast Legacy Linux-Based", - "ua" : "Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.81 Safari/537.36 CrKey/1.42.183786", - "expect" : - { - "name" : "Chromecast Linux", - "version" : "1.42.183786" - } - }, - { - "desc" : "Nintendo Switch", - "ua" : "Mozilla/5.0 (Nintendo Switch; WifiWebAuthApplet) AppleWebKit/606.4 (KHTML, like Gecko) NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393", - "expect" : - { - "name" : "Nintendo", - "version" : "Switch" - } - }, - { - "desc" : "PlayStation 4", - "ua" : "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", - "expect" : - { - "name" : "PlayStation", - "version" : "4" - } - }, - { - "desc" : "PlayStation 5", - "ua" : "Mozilla/5.0 (PlayStation 5/SmartTV) AppleWebKit/605.1.15 (KHTML, like Gecko)", - "expect" : - { - "name" : "PlayStation", - "version" : "5" - } - }, - { - "desc": "Pico 4", - "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", - "expect": { - "name" : "PICO", - "version" : "5.8.2" - } - }, - { - "desc": "Pico 4", - "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", - "expect": { - "name" : "PICO", - "version" : "5.4.0" - } - }, - { - "desc": "Pico Neo3 Link", - "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", - "expect": { - "name" : "Pico", - "version" : "5.8.4.0" - } - }, - { - "desc" : "Xbox 360", - "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox 360) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36", - "expect" : - { - "name" : "Xbox", - "version" : "360" - } - }, - { - "desc" : "Xbox One", - "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041", - "expect" : - { - "name" : "Xbox", - "version" : "One" - } - }, - { - "desc" : "Xbox X", - "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02", - "expect" : - { - "name" : "Xbox", - "version" : "X" - } - }, - { - "desc" : "Xbox Series X", - "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox Series X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02 ", - "expect" : - { - "name" : "Xbox", - "version" : "Series X" - } - }, - { - "desc" : "Xbox Series S", - "ua" : "Mozilla/5.0 (Compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Xbox; Xbox Series S)", - "expect" : - { - "name" : "Xbox", - "version" : "Series S" - } - }, - { - "desc" : "Mint", - "ua" : "Opera/9.80 (X11; Linux x86_64; Edition Linux Mint) Presto/2.12.388 Version/12.16", - "expect" : - { - "name" : "Mint", - "version" : "undefined" - } - }, - { - "desc" : "Mint", - "ua" : "Opera/9.64 (X11; Linux i686; U; Linux Mint; nb) Presto/2.1.1", - "expect" : - { - "name" : "Mint", - "version" : "undefined" - } - }, - { - "desc" : "Mint", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Linux Mint/6 (Felicia) Firefox/3.0.4", - "expect" : - { - "name" : "Mint", - "version" : "6" - } - }, - { - "desc" : "Ubuntu", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.22+ (KHTML, like Gecko) Chromium/17.0.963.56 Chrome/17.0.963.56 Safari/535.22+ Ubuntu/12.04 (3.4.1-0ubuntu1) Epiphany/3.4.1", - "expect" : - { - "name" : "Ubuntu", - "version" : "12.04" - } - }, - { - "desc" : "Ubuntu", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/31.0.1650.63 Chrome/31.0.1650.63 Safari/537.36", - "expect" : - { - "name" : "Ubuntu", - "version" : "undefined" - } - }, - { - "desc" : "Kubuntu", - "ua" : "Mozilla/5.0 (compatible; Konqueror/4.4; Linux 2.6.32-22-generic; X11; en_US) KHTML/4.4.3 (like Gecko) Kubuntu", - "expect" : - { - "name" : "Kubuntu", - "version" : "undefined" - } - }, - { - "desc" : "Debian", - "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.7 (like Gecko) (Debian)", - "expect" : - { - "name" : "Debian", - "version" : "undefined" - } - }, - { - "desc" : "Debian", - "ua" : "Mozilla/5.0 (X11; Linux x86_64; Debian GNU/Linux 8.1 (jessie)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36", - "expect" : - { - "name" : "Debian", - "version" : "8.1" - } - }, - { - "desc" : "Debian", - "ua" : "ELinks/0.12~pre5-4 (textmode; Debian; Linux 3.2.0-4-amd64 x86_64 192x47-2)", - "expect" : - { - "name" : "Debian", - "version" : "undefined" - } - }, - { - "desc" : "Debian", - "ua" : "w3m/0.5.3+debian-19", - "expect" : - { - "name" : "debian", - "version" : "19" - } - }, - { - "desc" : "Debian", - "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.3) Gecko/2008092814 (Debian-3.0.1-1)", - "expect" : - { - "name" : "Debian", - "version" : "3.0.1-1" - } - }, - { - "desc" : "Debian", - "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.24.4; X11) KHTML/3.5.9 (like Gecko) (Debian package 4:3.5.9.dfsg.1-2+b1)", - "expect" : - { - "name" : "Debian", - "version" : "undefined" - } - }, - { - "desc" : "OpenSUSE", - "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.17) Gecko/20110420 SUSE/3.6.17-0.2.1 Firefox/3.6.17", - "expect" : - { - "name" : "SUSE", - "version" : "3.6.17-0.2.1" - } - }, - { - "desc" : "Gentoo", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080716 (Gentoo) Galeon/2.0.6", - "expect" : - { - "name" : "Gentoo", - "version" : "undefined" - } - }, - { - "desc" : "Gentoo", - "ua" : "Xombrero (X11; U; Gentoo Linux amd64; en-US) Webkit/2.8.5", - "expect" : - { - "name" : "Gentoo", - "version" : "amd64" - } - }, - { - "desc" : "Gentoo", - "ua" : "Xombrero/1.6.4 (Linux amd64; en; Gentoo)", - "expect" : - { - "name" : "Gentoo", - "version" : "undefined" - } - }, - { - "desc" : "Gentoo", - "ua" : "Links (2.8; Linux 3.17.2-gentoo-x86 i686; GNU C 4.8.2; x)", - "expect" : - { - "name" : "gentoo", - "version" : "x86" - } - }, - { - "desc" : "Arch", - "ua" : "Uzbl (Webkit 1.1.10) (Arch Linux)", - "expect" : - { - "name" : "Arch", - "version" : "undefined" - } - }, - { - "desc" : "Slackware", - "ua" : "Mozilla/5.0 Slackware/13.37 (X11; U; Linux x86_64; en-US) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41", - "expect" : - { - "name" : "Slackware", - "version" : "13.37" - } - }, - { - "desc" : "Fedora", - "ua" : "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0", - "expect" : - { - "name" : "Fedora", - "version" : "undefined" - } - }, - { - "desc" : "Fedora", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:2.0) Gecko/20110404 Fedora/16-dev Firefox/4.0", - "expect" : - { - "name" : "Fedora", - "version" : "16-dev" - } - }, - { - "desc" : "Fedora", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; sk; rv:1.9.0.4) Gecko/2008111217 Fedora/3.0.4-1.fc10 Firefox/3.0.4", - "expect" : - { - "name" : "Fedora", - "version" : "3.0.4-1.fc10" - } - }, - { - "desc" : "Mandriva", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.22) Gecko/20110907 Mandriva Linux/1.9.2.22-0.1mdv2010.2 (2010.2) Firefox/3.6.22", - "expect" : - { - "name" : "Mandriva", - "version" : "1.9.2.22-0.1mdv2010.2" - } - }, - { - "desc" : "Chrome OS", - "ua" : "Mozilla/5.0 (X11; CrOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.0.0 Safari/537.36", - "expect" : - { - "name" : "Chrome OS", - "version" : "undefined" - } - }, - { - "desc" : "Chromium OS", - "ua" : "Mozilla/5.0 (X11; CrOS x86_64 10575.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", - "expect" : - { - "name" : "Chrome OS", - "version" : "10575.58.0" - } - }, - { - "desc" : "Fuchsia", - "ua" : "Mozilla/5.0 (X11; Fuchsia x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3557.0 Safari/537.36", - "expect" : - { - "name" : "Fuchsia", - "version" : "undefined" - } - }, - { - "desc" : "Solaris", - "ua" : "Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.7) Gecko/20070606", - "expect" : - { - "name" : "Solaris", - "version" : "sun4u" - } - }, - { - "desc" : "FreeBSD", - "ua" : "Mozilla/5.0 (X11; U; FreeBSD x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16", - "expect" : - { - "name" : "FreeBSD", - "version" : "undefined" - } - }, - { - "desc" : "OpenBSD", - "ua" : "Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.9.1) Gecko/20090702 Firefox/3.5", - "expect" : - { - "name" : "OpenBSD", - "version" : "undefined" - } - }, - { - "desc" : "NetBSD", - "ua" : "ELinks (0.4.3; NetBSD 3.0.2PATCH sparc64; 141x19)", - "expect" : - { - "name" : "NetBSD", - "version" : "3.0.2PATCH" - } - }, - { - "desc" : "DragonFly", - "ua" : "Mozilla/5.0 (X11; U; DragonFly i386; de; rv:1.9.1) Gecko/20090720 Firefox/3.5.1", - "expect" : - { - "name" : "DragonFly", - "version" : "undefined" - } - }, - { - "desc" : "iOS in App", - "ua" : "AppName/version CFNetwork/version Darwin/version", - "expect" : - { - "name" : "iOS", - "version" : "undefined" - } - }, - { - "desc" : "iOS with Chrome", - "ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3", - "expect" : - { - "name" : "iOS", - "version" : "5.1.1" - } - }, - { - "desc" : "iOS with Opera Mini", - "ua" : "Opera/9.80 (iPhone; Opera Mini/7.1.32694/27.1407; U; en) Presto/2.8.119 Version/11.10", - "expect" : - { - "name" : "iOS", - "version" : "undefined" - } - }, - { - "desc": "iOS with FaceBook Mobile App", - "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", - "expect": - { - "name" : "iOS", - "version" : "13.6.1" - } - }, - { - "desc": "iOS with Slack App", - "ua": "com.tinyspeck.chatlyio/23.04.10 (iPhone; iOS 16.4.1; Scale/3.00)", - "expect": - { - "name" : "iOS", - "version" : "16.4.1" - } - }, - { - "desc" : "watchOS", - "ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]", - "expect" : - { - "name" : "watchOS", - "version" : "8.4" - } - }, - { - "desc" : "watchOS", - "ua" : "atc/1.0 watchOS/7.4.1 model/Watch3,3 hwp/t8004 build/18T201 (6; dt:155)", - "expect" : - { - "name" : "watchOS", - "version" : "7.4.1" - } - }, - { - "desc" : "watchOS", - "ua" : "Watch4,3/5.3.8 (16U680)", - "expect" : - { - "name" : "watchOS", - "version" : "5.3.8" - } - }, - { - "desc" : "Mac OS on PowerPC", - "ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)", - "expect" : - { - "name" : "macOS", - "version" : "undefined" - } - }, - { - "desc" : "Mac OS X on x86, x86_64, or aarch64 using Firefox", - "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "macOS", - "version" : "x.y" - } - }, - { - "desc" : "Mac OS X on PowerPC using Firefox", - "ua" : "Mozilla/5.0 (Macintosh; PPC Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "macOS", - "version" : "x.y" - } - }, - { - "desc" : "Mac OS", - "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36", - "expect" : - { - "name" : "macOS", - "version" : "10.6.8" - } - }, - { - "desc" : "Haiku", - "ua" : "Mozilla/5.0 (Macintosh; Intel Haiku R1 x86) AppleWebKit/602.1.1 (KHTML, like Gecko) WebPositive/1.2 Version/8.0 Safari/602.1.1", - "expect" : - { - "name" : "Haiku", - "version" : "R1" - } - }, - { - "desc" : "KaiOS", - "ua" : "Mozilla/5.0 (Mobile; Nokia_8110_4G; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5", - "expect" : - { - "name" : "KAIOS", - "version" : "2.5" - } - }, - { - "desc" : "iTunes Windows Vista", - "ua" : "iTunes/10.7 (Windows; Microsoft Windows Vista Home Premium Edition Service Pack 1 (Build 6001)) AppleWebKit/536.26.9", - "expect" : - { - "name" : "Windows", - "version" : "Vista" - } - }, - { - "desc" : "iOS BE App", - "ua" : "APP-BE Test/1.0 (iPad; Apple; CPU iPhone OS 7_0_2 like Mac OS X)", - "expect" : - { - "name" : "iOS", - "version" : "7.0.2" - } - }, - { - "desc" : "KTB-Nexus 5", - "ua" : "APP-My App/1.0 (Linux; Android 4.2.1; Nexus 5 Build/JOP40D)", - "expect" : - { - "name" : "Android", - "version" : "4.2.1" - } - }, - { - "desc" : "Solaris", - "ua" : "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)", - "expect" : - { - "name" : "Solaris", - "version" : "4.1.4" - } - }, - { - "desc" : "Raspbian", - "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Raspbian Chromium/72.0.3626.121 HeadlessChrome/72.0.3626.121 Safari/537.36", - "expect" : - { - "name" : "Raspbian", - "version" : "undefined" - } - }, - { - "desc" : "Raspbian", - "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/538.15 (KHTML, like Gecko) Version/8.0 Safari/538.15 Raspbian/9.0 (1:3.8.2.0-0rpi28) Epiphany/3.8.2", - "expect" : - { - "name" : "Raspbian", - "version" : "9.0" - } - }, - { - "desc" : "AIX", - "ua" : "Mozilla/5.0 (X11; U; AIX 000138384C00; en-US; rv:1.0.1) Gecko/20030213 Netscape/7.0", - "expect" : - { - "name" : "AIX", - "version" : "undefined" - } - }, - { - "desc" : "Plan9", - "ua" : "NCSA_Mosaic/5.0 (X11;Plan 9 4.0)", - "expect" : - { - "name" : "Plan 9", - "version" : "4.0" - } - }, - { - "desc" : "Minix", - "ua" : "Mozilla/5.0 (X11; Original ; Minix 3.3 ; rv:3.0)", - "expect" : - { - "name" : "Minix", - "version" : "3.3" - } - }, - { - "desc" : "BeOS", - "ua" : "Mozilla/5.0 (BeOS; U; BeOS BePC; en-US; rv:1.8.1.8pre) Gecko/20070926 SeaMonkey/1.1.5pre", - "expect" : - { - "name" : "BeOS", - "version" : "undefined" - } - }, - { - "desc" : "OS/2", - "ua" : "Links (2.1pre14; OS/2 1 i386; 80x33)", - "expect" : - { - "name" : "OS/2", - "version" : "undefined" - } - }, - { - "desc" : "AmigaOS", - "ua" : "Mozilla/4.0 (compatible; AWEB 3.4 SE; AmigaOS)", - "expect" : - { - "name" : "AmigaOS", - "version" : "undefined" - } - }, - { - "desc" : "MorphOS", - "ua" : "AmigaVoyager/3.4.4 (MorphOS/PPC native)", - "expect" : - { - "name" : "MorphOS", - "version" : "undefined" - } - }, - { - "desc" : "UNIX", - "ua" : "Surf/0.4.1 (X11; U; Unix; en-US) AppleWebKit/531.2+ Compatible (Safari)", - "expect" : - { - "name" : "Unix", - "version" : "undefined" - } - }, - { - "desc" : "Joli", - "ua" : "Mozilla/5.0 (X11; Jolicloud Linux i686) AppleWebKit/537.6 (KHTML, like Gecko) Joli OS/1.2 Chromium/23.0.1240.0 Chrome/23.0.1240.0 Safari/537.6", - "expect" : - { - "name" : "Joli", - "version" : "1.2" - } - }, - { - "desc" : "CentOS", - "ua" : "Konqueror/15.13 (CentOS Linux 7.4; cs-CZ;)", - "expect" : - { - "name" : "CentOS", - "version" : "7.4" - } - }, - { - "desc" : "PCLinuxOS", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.13) Gecko/20101209 PCLinuxOS/1.9.2.13-1pclos2010 (2010) Firefox/3.6.13", - "expect" : - { - "name" : "PCLinuxOS", - "version" : "1.9.2.13-1pclos2010" - } - }, - { - "desc" : "RedHat", - "ua" : "Mozilla/5.0 (compatible; Konqueror/4.3; Linux) KHTML/4.3.4 (like Gecko) Red Hat Enterprise Linux/4.3.4-11.el6_1.4", - "expect" : - { - "name" : "Red Hat", - "version" : "4.3.4-11.el6_1.4" - } - }, - { - "desc" : "RedHat", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.13pre) Gecko/20070717 Red Hat/1.0.9-4.el4 SeaMonkey/1.0.9", - "expect" : - { - "name" : "Red Hat", - "version" : "1.0.9-4.el4" - } - }, - { - "desc" : "RedHat", - "ua" : "iTunes/4.7.1 (Linux; N; Red Hat; x86_64-linux; EN; utf8) SqueezeCenter, Squeezebox Server, Logitech Media Server/7.9.1/1522157629", - "expect" : - { - "name" : "Red Hat", - "version" : "undefined" - } - }, - { - "desc" : "RedHat", - "ua" : "curl/7.20.0 (x86_64-redhat-linux-gnu) libcurl/7.20.0 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5", - "expect" : - { - "name" : "redhat", - "version" : "undefined" - } - }, - { - "desc" : "RISC OS", - "ua" : "Mozilla/1.10 [en] (Compatible; RISC OS 3.70; Oregano 1.10)", - "expect" : - { - "name" : "RISC OS", - "version" : "3.70" - } - }, - { - "desc" : "Zenwalk", - "ua" : "Flock/2.16 (Zenwalk 7.3; es_PR;)", - "expect" : - { - "name" : "Zenwalk", - "version" : "7.3" - } - }, - { - "desc" : "Hurd", - "ua" : "Mozilla/5.0 (X11; Hurd 0.9 i386; en-US) libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.0 Safari/696.96", - "expect" : - { - "name" : "Hurd", - "version" : "0.9" - } - }, - { - "desc" : "Linux", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36", - "expect" : - { - "name" : "Linux", - "version" : "x86_64" - } - }, - { - "desc" : "Deepin", - "ua" : "Mozilla/5.0 (X11; Linux x86_64; Deepin 15.5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36 NFSBrowser/5.0.0.1886", - "expect" : - { - "name" : "Deepin", - "version" : "15.5" - } - }, - { - "desc" : "Palm OS", - "ua" : "Mozilla/4.76 [en] (PalmOS; U; WebPro3.0; Palm-Arz1)", - "expect" : - { - "name" : "Palm", - "version" : "undefined" - } - }, - { - "desc" : "Panasonic Viera", - "ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", - "expect" : - { - "name" : "VIERA", - "version" : "undefined" - } - }, - { - "desc" : "Netrange Smart TV", - "ua" : "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", - "expect" : - { - "name" : "NETRANGE", - "version" : "undefined" - } - }, - { - "desc" : "NetTV 3.2.1", - "ua" : "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70", - "expect" : - { - "name" : "NETTV", - "version" : "3.2.1" - } - }, - { - "desc" : "HP-UX", - "ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0", - "expect" : - { - "name" : "HP-UX", - "version" : "undefined" - } - }, - { - "desc" : "Contiki", - "ua" : "Contiki/1.0 (Commodore 64; http://dunkels.com/adam/contiki/)", - "expect" : - { - "name" : "Contiki", - "version" : "1.0" - } - }, - { - "desc" : "Linpus", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b5pre) Gecko/2008032619 Linpus/3.0-0.49", - "expect" : - { - "name" : "Linpus", - "version" : "3.0-0.49" - } - }, - { - "desc" : "Manjaro", - "ua" : "Mozilla/5.0 (X11; Manjaro 19.0.2; Arch; x64; rv:84.0) Gecko/20100101 Firefox/84.0", - "expect" : - { - "name" : "Manjaro", - "version" : "19.0.2" - } - }, - { - "desc" : "elementary OS", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/604.1 (KHTML, like Gecko) Version/11.0 Safari/604.1 elementary OS/0.4 (Loki) Epiphany/3.18.11", - "expect" : - { - "name" : "elementary OS", - "version" : "0.4" - } - }, - { - "desc" : "GhostBSD", - "ua" : "Mozilla/5.0 (X11; GhostBSD/10.3; x86_64; rv:50.0.1) Gecko/20100101 Firefox/50.0.1", - "expect" : - { - "name" : "GhostBSD", - "version" : "10.3" - } - }, - { - "desc" : "Android-x86", - "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Generic Android-x86) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 OPR/61.2.3076.56749", - "expect" : - { - "name" : "Android-x86", - "version" : "7.1.2" - } - }, - { - "desc" : "Sabayon", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Sabayon Chrome/19.0.1084.46 Safari/536.5", - "expect" : - { - "name" : "Sabayon", - "version" : "undefined" - } - }, - { - "desc" : "Linspire", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060803 Firefox/1.5.0.4 Linspire/1.5.0.4", - "expect" : - { - "name" : "Linspire", - "version" : "1.5.0.4" - } - }, - { - "desc" : "SerenityOS", - "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb", - "expect" : - { - "name" : "SerenityOS", - "version" : "undefined" - } - }, - { - "desc" : "OpenHarmony", - "ua" : "Mozilla/5.0 (Phone; OpenHarmony 4.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile", - "expect" : - { - "name" : "OpenHarmony", - "version" : "4.1" - } - } -] diff --git a/test/specs/os/os2.json b/test/specs/os/os2.json new file mode 100644 index 000000000..cb38c1183 --- /dev/null +++ b/test/specs/os/os2.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "OS/2", + "ua" : "Links (2.1pre14; OS/2 1 i386; 80x33)", + "expect" : + { + "name" : "OS/2", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/palm.json b/test/specs/os/palm.json new file mode 100644 index 000000000..5fc831ebc --- /dev/null +++ b/test/specs/os/palm.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Palm OS", + "ua" : "Mozilla/4.76 [en] (PalmOS; U; WebPro3.0; Palm-Arz1)", + "expect" : + { + "name" : "Palm", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/pclinuxos.json b/test/specs/os/pclinuxos.json new file mode 100644 index 000000000..521b4be9b --- /dev/null +++ b/test/specs/os/pclinuxos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "PCLinuxOS", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.13) Gecko/20101209 PCLinuxOS/1.9.2.13-1pclos2010 (2010) Firefox/3.6.13", + "expect" : + { + "name" : "PCLinuxOS", + "version" : "1.9.2.13-1pclos2010" + } + } +] \ No newline at end of file diff --git a/test/specs/os/pico.json b/test/specs/os/pico.json new file mode 100644 index 000000000..456a5fa47 --- /dev/null +++ b/test/specs/os/pico.json @@ -0,0 +1,26 @@ +[ + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "name" : "PICO", + "version" : "5.8.2" + } + }, + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect": { + "name" : "PICO", + "version" : "5.4.0" + } + }, + { + "desc": "Pico Neo3 Link", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "name" : "Pico", + "version" : "5.8.4.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/plan9.json b/test/specs/os/plan9.json new file mode 100644 index 000000000..e3726fb9a --- /dev/null +++ b/test/specs/os/plan9.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Plan9", + "ua" : "NCSA_Mosaic/5.0 (X11;Plan 9 4.0)", + "expect" : + { + "name" : "Plan 9", + "version" : "4.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/playstation.json b/test/specs/os/playstation.json new file mode 100644 index 000000000..38cf97fe4 --- /dev/null +++ b/test/specs/os/playstation.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "PlayStation 4", + "ua" : "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", + "expect" : + { + "name" : "PlayStation", + "version" : "4" + } + }, + { + "desc" : "PlayStation 5", + "ua" : "Mozilla/5.0 (PlayStation 5/SmartTV) AppleWebKit/605.1.15 (KHTML, like Gecko)", + "expect" : + { + "name" : "PlayStation", + "version" : "5" + } + } +] \ No newline at end of file diff --git a/test/specs/os/qnx.json b/test/specs/os/qnx.json new file mode 100644 index 000000000..fcb3ed1c8 --- /dev/null +++ b/test/specs/os/qnx.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "QNX", + "ua" : "Mozilla/5.0 (Photon; U; QNX x86pc; en-US; rv:1.8.1.20) Gecko/20090127 BonEcho/2.0.0.20", + "expect" : + { + "name" : "QNX", + "version" : "x86pc" + } + } +] \ No newline at end of file diff --git a/test/specs/os/raspbian.json b/test/specs/os/raspbian.json new file mode 100644 index 000000000..10fd9fb11 --- /dev/null +++ b/test/specs/os/raspbian.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Raspbian", + "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Raspbian Chromium/72.0.3626.121 HeadlessChrome/72.0.3626.121 Safari/537.36", + "expect" : + { + "name" : "Raspbian", + "version" : "undefined" + } + }, + { + "desc" : "Raspbian", + "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/538.15 (KHTML, like Gecko) Version/8.0 Safari/538.15 Raspbian/9.0 (1:3.8.2.0-0rpi28) Epiphany/3.8.2", + "expect" : + { + "name" : "Raspbian", + "version" : "9.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/redhat.json b/test/specs/os/redhat.json new file mode 100644 index 000000000..89f3e25bd --- /dev/null +++ b/test/specs/os/redhat.json @@ -0,0 +1,38 @@ +[ + { + "desc" : "RedHat", + "ua" : "Mozilla/5.0 (compatible; Konqueror/4.3; Linux) KHTML/4.3.4 (like Gecko) Red Hat Enterprise Linux/4.3.4-11.el6_1.4", + "expect" : + { + "name" : "Red Hat", + "version" : "4.3.4-11.el6_1.4" + } + }, + { + "desc" : "RedHat", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.13pre) Gecko/20070717 Red Hat/1.0.9-4.el4 SeaMonkey/1.0.9", + "expect" : + { + "name" : "Red Hat", + "version" : "1.0.9-4.el4" + } + }, + { + "desc" : "RedHat", + "ua" : "iTunes/4.7.1 (Linux; N; Red Hat; x86_64-linux; EN; utf8) SqueezeCenter, Squeezebox Server, Logitech Media Server/7.9.1/1522157629", + "expect" : + { + "name" : "Red Hat", + "version" : "undefined" + } + }, + { + "desc" : "RedHat", + "ua" : "curl/7.20.0 (x86_64-redhat-linux-gnu) libcurl/7.20.0 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5", + "expect" : + { + "name" : "redhat", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/rim-tablet-os.json b/test/specs/os/rim-tablet-os.json new file mode 100644 index 000000000..4451c3cda --- /dev/null +++ b/test/specs/os/rim-tablet-os.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "RIM Tablet OS", + "ua" : "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+", + "expect" : + { + "name" : "RIM Tablet OS", + "version" : "2.1.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/risc-os.json b/test/specs/os/risc-os.json new file mode 100644 index 000000000..5442784e3 --- /dev/null +++ b/test/specs/os/risc-os.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "RISC OS", + "ua" : "Mozilla/1.10 [en] (Compatible; RISC OS 3.70; Oregano 1.10)", + "expect" : + { + "name" : "RISC OS", + "version" : "3.70" + } + } +] \ No newline at end of file diff --git a/test/specs/os/sabayon.json b/test/specs/os/sabayon.json new file mode 100644 index 000000000..d44f9e2d3 --- /dev/null +++ b/test/specs/os/sabayon.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Sabayon", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Sabayon Chrome/19.0.1084.46 Safari/536.5", + "expect" : + { + "name" : "Sabayon", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/sailfish.json b/test/specs/os/sailfish.json new file mode 100644 index 000000000..f96376be4 --- /dev/null +++ b/test/specs/os/sailfish.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Sailfish", + "ua" : "Mozilla/5.0 (Linux; U; Sailfish 3.0; Mobile; rv:45.0) Gecko/45.0 Firefox/45.0 SailfishBrowser/1.0", + "expect" : + { + "name" : "Sailfish", + "version" : "3.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/serenityos.json b/test/specs/os/serenityos.json new file mode 100644 index 000000000..748546e39 --- /dev/null +++ b/test/specs/os/serenityos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "SerenityOS", + "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb", + "expect" : + { + "name" : "SerenityOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/series40.json b/test/specs/os/series40.json new file mode 100644 index 000000000..1b02c1538 --- /dev/null +++ b/test/specs/os/series40.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Series40", + "ua" : "Mozilla/5.0 (Series40; Nokia2055/03.20; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/2.2.0.0.34", + "expect" : + { + "name" : "Series40", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/slackware.json b/test/specs/os/slackware.json new file mode 100644 index 000000000..35f446e69 --- /dev/null +++ b/test/specs/os/slackware.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Slackware", + "ua" : "Mozilla/5.0 Slackware/13.37 (X11; U; Linux x86_64; en-US) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41", + "expect" : + { + "name" : "Slackware", + "version" : "13.37" + } + } +] \ No newline at end of file diff --git a/test/specs/os/solaris.json b/test/specs/os/solaris.json new file mode 100644 index 000000000..450e47dbf --- /dev/null +++ b/test/specs/os/solaris.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Solaris", + "ua" : "Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.7) Gecko/20070606", + "expect" : + { + "name" : "Solaris", + "version" : "sun4u" + } + }, + { + "desc" : "Solaris", + "ua" : "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)", + "expect" : + { + "name" : "Solaris", + "version" : "4.1.4" + } + } +] \ No newline at end of file diff --git a/test/specs/os/suse.json b/test/specs/os/suse.json new file mode 100644 index 000000000..9da3f3596 --- /dev/null +++ b/test/specs/os/suse.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "OpenSUSE", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.17) Gecko/20110420 SUSE/3.6.17-0.2.1 Firefox/3.6.17", + "expect" : + { + "name" : "SUSE", + "version" : "3.6.17-0.2.1" + } + } +] \ No newline at end of file diff --git a/test/specs/os/symbian.json b/test/specs/os/symbian.json new file mode 100644 index 000000000..24f509e25 --- /dev/null +++ b/test/specs/os/symbian.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Symbian", + "ua" : "Nokia5250/10.0.011 (SymbianOS/9.4; U; Series60/5.0 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/525 (KHTML, like Gecko) Safari/525 3gpp-gba", + "expect" : + { + "name" : "Symbian", + "version" : "9.4" + } + }, + { + "desc" : "Symbian", + "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaC7-00/024.001; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.37 Mobile Safari/533.4 3gpp-gba", + "expect" : + { + "name" : "Symbian", + "version" : "5.2" + } + } +] \ No newline at end of file diff --git a/test/specs/os/tizen.json b/test/specs/os/tizen.json new file mode 100644 index 000000000..4f9320d7c --- /dev/null +++ b/test/specs/os/tizen.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Tizen", + "ua" : "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", + "expect" : + { + "name" : "Tizen", + "version" : "2.3" + } + }, + { + "desc" : "Tizen", + "ua" : "Mozilla/5.0 (Linux; Tizen 2.3; SAMSUNG SM-Z130H) AppleWebKit/537.3 (KHTML, like Gecko) Version/2.3 Mobile Safari/537.3", + "expect" : + { + "name" : "Tizen", + "version" : "2.3" + } + }, + { + "desc" : "Tizen 6.0", + "ua" : "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", + "expect" : + { + "name" : "Tizen", + "version" : "6.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/ubuntu.json b/test/specs/os/ubuntu.json new file mode 100644 index 000000000..d3958f9fc --- /dev/null +++ b/test/specs/os/ubuntu.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.22+ (KHTML, like Gecko) Chromium/17.0.963.56 Chrome/17.0.963.56 Safari/535.22+ Ubuntu/12.04 (3.4.1-0ubuntu1) Epiphany/3.4.1", + "expect" : + { + "name" : "Ubuntu", + "version" : "12.04" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/31.0.1650.63 Chrome/31.0.1650.63 Safari/537.36", + "expect" : + { + "name" : "Ubuntu", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/unix.json b/test/specs/os/unix.json new file mode 100644 index 000000000..18e733208 --- /dev/null +++ b/test/specs/os/unix.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "UNIX", + "ua" : "Surf/0.4.1 (X11; U; Unix; en-US) AppleWebKit/531.2+ Compatible (Safari)", + "expect" : + { + "name" : "Unix", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/watchos.json b/test/specs/os/watchos.json new file mode 100644 index 000000000..ec2c30b55 --- /dev/null +++ b/test/specs/os/watchos.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "watchOS", + "ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]", + "expect" : + { + "name" : "watchOS", + "version" : "8.4" + } + }, + { + "desc" : "watchOS", + "ua" : "atc/1.0 watchOS/7.4.1 model/Watch3,3 hwp/t8004 build/18T201 (6; dt:155)", + "expect" : + { + "name" : "watchOS", + "version" : "7.4.1" + } + }, + { + "desc" : "watchOS", + "ua" : "Watch4,3/5.3.8 (16U680)", + "expect" : + { + "name" : "watchOS", + "version" : "5.3.8" + } + } +] \ No newline at end of file diff --git a/test/specs/os/webos.json b/test/specs/os/webos.json new file mode 100644 index 000000000..c31cd3f65 --- /dev/null +++ b/test/specs/os/webos.json @@ -0,0 +1,65 @@ +[ + { + "desc" : "WebOS", + "ua" : "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.5; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.83 Safari/534.6 TouchPad/1.0", + "expect" : + { + "name" : "webOS", + "version" : "3.0.5" + } + }, + { + "desc" : "WebOS", + "ua" : "Mozilla/5.0 (webOS/1.4.5; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.0", + "expect" : + { + "name" : "webOS", + "version" : "1.4.5" + } + }, + { + "desc" : "WebOS TV 5.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 WebAppManager", + "expect" : + { + "name" : "webOS", + "version" : "TV" + } + }, + { + "desc" : "WebOS TV 4.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.34 Safari/537.36 WebAppManager", + "expect" : + { + "name" : "webOS", + "version" : "TV" + } + }, + { + "desc" : "WebOS TV 3.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.2.1 Chrome/38.0.2125.122 Safari/537.36 WebAppManager", + "expect" : + { + "name" : "webOS", + "version" : "TV" + } + }, + { + "desc" : "WebOS TV 2.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/538.2 (KHTML, like Gecko) Large Screen WebAppManager Safari/538.2", + "expect" : + { + "name" : "webOS", + "version" : "TV" + } + }, + { + "desc" : "WebOS TV 1.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen WebAppManager Safari/537.41", + "expect" : + { + "name" : "webOS", + "version" : "TV" + } + } +] \ No newline at end of file diff --git a/test/specs/os/windows-mobile.json b/test/specs/os/windows-mobile.json new file mode 100644 index 000000000..5baf78c67 --- /dev/null +++ b/test/specs/os/windows-mobile.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Windows Mobile", + "ua" : "Mozilla/5.0 (ZTE-E_N72/N72V1.0.0B02;U;Windows Mobile/6.1;Profile/MIDP-2.0 Configuration/CLDC-1.1;320*240;CTC/2.0) IE/6.0 (compatible; MSIE 4.01; Windows CE; PPC)/UC Browser7.7.1.88", + "expect" : + { + "name" : "Windows Mobile", + "version" : "6.1" + } + }, + { + "desc" : "Windows Mobile", + "ua" : "Opera/9.80 (Windows Mobile; WCE; Opera Mobi/WMD-50433; U; en) Presto/2.4.13 Version/10.00", + "expect" : + { + "name" : "Windows Mobile", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/specs/os/windows-phone.json b/test/specs/os/windows-phone.json new file mode 100644 index 000000000..a6a8b8212 --- /dev/null +++ b/test/specs/os/windows-phone.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Windows Phone", + "ua" : "Opera/9.80 (Windows Phone; Opera Mini/7.6.8/35.7518; U; ru) Presto/2.8.119 Version/11.10", + "expect" : + { + "name" : "Windows Phone", + "version" : "undefined" + } + }, + { + "desc" : "Windows Phone OS", + "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; DELL; Venue Pro)", + "expect" : + { + "name" : "Windows Phone OS", + "version" : "7.0" + } + }, + { + "desc" : "Windows Phone 8", + "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; HTC; Windows Phone 8X by HTC)", + "expect" : + { + "name" : "Windows Phone", + "version" : "8.0" + } + } +] \ No newline at end of file diff --git a/test/specs/os/windows.json b/test/specs/os/windows.json new file mode 100644 index 000000000..39e954155 --- /dev/null +++ b/test/specs/os/windows.json @@ -0,0 +1,146 @@ +[ + { + "desc" : "Windows 95", + "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 98", + "ua" : "Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)", + "expect" : + { + "name" : "Windows", + "version" : "98" + } + }, + { + "desc" : "Windows ME", + "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90) Gecko/20020502 CS 2000 7.0/7.0", + "expect" : + { + "name" : "Windows", + "version" : "ME" + } + }, + { + "desc" : "Windows 2000", + "ua" : "Mozilla/3.0 (compatible; MSIE 3.0; Windows NT 5.0)", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Windows XP", + "ua" : "Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 5.2)", + "expect" : + { + "name" : "Windows", + "version" : "XP" + } + }, + { + "desc" : "Windows Vista", + "ua" : "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; fr-FR)", + "expect" : + { + "name" : "Windows", + "version" : "Vista" + } + }, + { + "desc" : "Windows 7", + "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "Windows 8", + "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C)", + "expect" : + { + "name" : "Windows", + "version" : "8" + } + }, + { + "desc" : "Windows 10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "WeChat Desktop for Windows Built-in Browser", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "WeChat Desktop for Windows Built-in Browser major version in 4", + "ua" : "mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/81.0.4044.138 safari/537.36 nettype/wifi micromessenger/7.0.20.1781(0x6700143b) windowswechat", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "Windows RT", + "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", + "expect" : + { + "name" : "Windows", + "version" : "RT" + } + }, + { + "desc" : "Windows CE", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)", + "expect" : + { + "name" : "Windows", + "version" : "CE" + } + }, + { + "desc" : "Windows NT on x86 or aarch64 CPU using Firefox", + "ua" : "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0", + "expect" : + { + "name" : "Windows", + "version" : "NT x" + } + }, + { + "desc" : "Windows NT on x64 CPU using Firefox", + "ua" : "Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0", + "expect" : + { + "name" : "Windows", + "version" : "NT x" + } + }, + { + "desc" : "iTunes Windows Vista", + "ua" : "iTunes/10.7 (Windows; Microsoft Windows Vista Home Premium Edition Service Pack 1 (Build 6001)) AppleWebKit/536.26.9", + "expect" : + { + "name" : "Windows", + "version" : "Vista" + } + } +] \ No newline at end of file diff --git a/test/specs/os/xbox.json b/test/specs/os/xbox.json new file mode 100644 index 000000000..f8f9f6784 --- /dev/null +++ b/test/specs/os/xbox.json @@ -0,0 +1,47 @@ +[ + { + "desc" : "Xbox 360", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox 360) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36", + "expect" : + { + "name" : "Xbox", + "version" : "360" + } + }, + { + "desc" : "Xbox One", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041", + "expect" : + { + "name" : "Xbox", + "version" : "One" + } + }, + { + "desc" : "Xbox X", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02", + "expect" : + { + "name" : "Xbox", + "version" : "X" + } + }, + { + "desc" : "Xbox Series X", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox Series X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02 ", + "expect" : + { + "name" : "Xbox", + "version" : "Series X" + } + }, + { + "desc" : "Xbox Series S", + "ua" : "Mozilla/5.0 (Compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Xbox; Xbox Series S)", + "expect" : + { + "name" : "Xbox", + "version" : "Series S" + } + } +] \ No newline at end of file diff --git a/test/specs/os/zenwalk.json b/test/specs/os/zenwalk.json new file mode 100644 index 000000000..230b5ddd9 --- /dev/null +++ b/test/specs/os/zenwalk.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Zenwalk", + "ua" : "Flock/2.16 (Zenwalk 7.3; es_PR;)", + "expect" : + { + "name" : "Zenwalk", + "version" : "7.3" + } + } +] \ No newline at end of file From aa56b2f4f39fa00e17c52cf207c9bad06f0a9ed8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 28 Nov 2024 12:53:48 +0700 Subject: [PATCH 292/388] Update `engine.version` to be the same as `browser.version` for Chromium-based browser when UA-CH is available --- src/main/ua-parser.js | 7 ++++++- test/mocha-test.js | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 067a3e367..701375b4b 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -85,6 +85,7 @@ PREFIX_MOBILE = 'Mobile ', SUFFIX_BROWSER = ' Browser', CHROME = 'Chrome', + CHROMIUM = 'Chromium', CHROMECAST = 'Chromecast', EDGE = 'Edge', FIREFOX = 'Firefox', @@ -1161,17 +1162,21 @@ switch (this.itemType) { case UA_BROWSER: + case UA_ENGINE: var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; if (brands) { for (var i in brands) { var brandName = strip(/(Google|Microsoft) /, brands[i].brand || brands[i]), brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && !/chromi/i.test(brandName)))) { + if (this.itemType == UA_BROWSER && !/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && brandName != CHROMIUM))) { this.set(NAME, brandName) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); prevName = brandName; } + if (this.itemType == UA_ENGINE && brandName == CHROMIUM) { + this.set(VERSION, brandVersion); + } } } break; diff --git a/test/mocha-test.js b/test/mocha-test.js index 8f6d5290d..9c50a8200 100644 --- a/test/mocha-test.js +++ b/test/mocha-test.js @@ -9,7 +9,7 @@ var browsers = require('./specs/browser/browser-all.json'); var cpus = require('./specs/cpu/cpu-all.json'); var devices = readJsonFiles('test/specs/device'); var engines = require('./specs/engine/engine-all.json'); -var os = require('./specs/os/os-all.json'); +var os = readJsonFiles('test/specs/os'); var { Headers } = require('node-fetch'); function readJsonFiles(dir) { @@ -395,9 +395,9 @@ describe('Map UA-CH headers', function () { assert.strictEqual(device.model, "Pixel 99"); assert.strictEqual(device.vendor, undefined); assert.strictEqual(uap.engine.name, 'Blink'); - assert.strictEqual(uap.engine.version, '110.0.0.0'); + assert.strictEqual(uap.engine.version, '93.0.1.2'); assert.strictEqual(engine.name, 'Blink'); - assert.strictEqual(engine.version, '110.0.0.0'); + assert.strictEqual(engine.version, '93.0.1.2'); assert.strictEqual(uap.os.name, "Windows"); assert.strictEqual(uap.os.version, "11"); assert.strictEqual(os.name, "Windows"); From 1a057b4be24068e640264f4348f3d70d46d61704 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 28 Nov 2024 23:24:19 +0700 Subject: [PATCH 293/388] Reorganize test files & directories --- package.json | 8 ++++---- test/{specs => data/ua}/browser/browser-all.json | 0 test/{specs => data/ua}/cpu/cpu-all.json | 0 test/{specs => data/ua}/device/_others.json | 0 test/{specs => data/ua}/device/acer.json | 0 test/{specs => data/ua}/device/advan.json | 0 test/{specs => data/ua}/device/alcatel.json | 0 test/{specs => data/ua}/device/amazon.json | 0 test/{specs => data/ua}/device/apple.json | 0 test/{specs => data/ua}/device/asus.json | 0 test/{specs => data/ua}/device/blackberry.json | 0 test/{specs => data/ua}/device/cat.json | 0 test/{specs => data/ua}/device/energizer.json | 0 test/{specs => data/ua}/device/facebook.json | 0 test/{specs => data/ua}/device/fairphone.json | 0 test/{specs => data/ua}/device/google.json | 0 test/{specs => data/ua}/device/hmd.json | 0 test/{specs => data/ua}/device/honor.json | 0 test/{specs => data/ua}/device/htc.json | 0 test/{specs => data/ua}/device/huawei.json | 0 test/{specs => data/ua}/device/imo.json | 0 test/{specs => data/ua}/device/infinix.json | 0 test/{specs => data/ua}/device/itel.json | 0 test/{specs => data/ua}/device/jolla.json | 0 test/{specs => data/ua}/device/kobo.json | 0 test/{specs => data/ua}/device/lenovo.json | 0 test/{specs => data/ua}/device/lg.json | 0 test/{specs => data/ua}/device/meizu.json | 0 test/{specs => data/ua}/device/micromax.json | 0 test/{specs => data/ua}/device/microsoft.json | 0 test/{specs => data/ua}/device/motorola.json | 0 test/{specs => data/ua}/device/nintendo.json | 0 test/{specs => data/ua}/device/nokia.json | 0 test/{specs => data/ua}/device/nothing.json | 0 test/{specs => data/ua}/device/nvidia.json | 0 test/{specs => data/ua}/device/oneplus.json | 0 test/{specs => data/ua}/device/oppo.json | 0 test/{specs => data/ua}/device/ouya.json | 0 test/{specs => data/ua}/device/panasonic.json | 0 test/{specs => data/ua}/device/pico.json | 0 test/{specs => data/ua}/device/polytron.json | 0 test/{specs => data/ua}/device/realme.json | 0 test/{specs => data/ua}/device/roku.json | 0 test/{specs => data/ua}/device/samsung.json | 0 test/{specs => data/ua}/device/sharp.json | 0 test/{specs => data/ua}/device/smartfren.json | 0 test/{specs => data/ua}/device/sony.json | 0 test/{specs => data/ua}/device/tcl.json | 0 test/{specs => data/ua}/device/technisat.json | 0 test/{specs => data/ua}/device/tecno.json | 0 test/{specs => data/ua}/device/tesla.json | 0 test/{specs => data/ua}/device/ulefone.json | 0 test/{specs => data/ua}/device/vivo.json | 0 test/{specs => data/ua}/device/xiaomi.json | 0 test/{specs => data/ua}/device/zte.json | 0 test/{specs => data/ua}/engine/engine-all.json | 0 test/{specs => data/ua}/extension/cli.json | 0 test/{specs => data/ua}/extension/crawler.json | 0 test/{specs => data/ua}/extension/email.json | 0 .../ua}/extension/extra-devices.json | 0 test/{specs => data/ua}/extension/fetcher.json | 0 test/{specs => data/ua}/extension/library.json | 0 test/{specs => data/ua}/extension/mediaplayer.json | 0 test/{specs => data/ua}/os/aix.json | 0 test/{specs => data/ua}/os/amigaos.json | 0 test/{specs => data/ua}/os/android-x86.json | 0 test/{specs => data/ua}/os/android.json | 0 test/{specs => data/ua}/os/arch.json | 0 test/{specs => data/ua}/os/bada.json | 0 test/{specs => data/ua}/os/beos.json | 0 test/{specs => data/ua}/os/blackberry.json | 0 test/{specs => data/ua}/os/centos.json | 0 test/{specs => data/ua}/os/chrome-os.json | 0 test/{specs => data/ua}/os/chromecast-android.json | 0 test/{specs => data/ua}/os/chromecast-fuchsia.json | 0 test/{specs => data/ua}/os/chromecast-linux.json | 0 .../ua}/os/chromecast-smartspeaker.json | 0 test/{specs => data/ua}/os/contiki.json | 0 test/{specs => data/ua}/os/debian.json | 0 test/{specs => data/ua}/os/deepin.json | 0 test/{specs => data/ua}/os/dragonfly.json | 0 test/{specs => data/ua}/os/elementary-os.json | 0 test/{specs => data/ua}/os/fedora.json | 0 test/{specs => data/ua}/os/firefox-os.json | 0 test/{specs => data/ua}/os/freebsd.json | 0 test/{specs => data/ua}/os/fuchsia.json | 0 test/{specs => data/ua}/os/gentoo.json | 0 test/{specs => data/ua}/os/ghostbsd.json | 0 test/{specs => data/ua}/os/haiku.json | 0 test/{specs => data/ua}/os/harmonyos.json | 0 test/{specs => data/ua}/os/hp-ux.json | 0 test/{specs => data/ua}/os/hurd.json | 0 test/{specs => data/ua}/os/ios.json | 0 test/{specs => data/ua}/os/joli.json | 0 test/{specs => data/ua}/os/kaios.json | 0 test/{specs => data/ua}/os/kubuntu.json | 0 test/{specs => data/ua}/os/linpus.json | 0 test/{specs => data/ua}/os/linspire.json | 0 test/{specs => data/ua}/os/linux.json | 0 test/{specs => data/ua}/os/macos.json | 0 test/{specs => data/ua}/os/maemo.json | 0 test/{specs => data/ua}/os/mandriva.json | 0 test/{specs => data/ua}/os/manjaro.json | 0 test/{specs => data/ua}/os/meego.json | 0 test/{specs => data/ua}/os/minix.json | 0 test/{specs => data/ua}/os/mint.json | 0 test/{specs => data/ua}/os/morphos.json | 0 test/{specs => data/ua}/os/netbsd.json | 0 test/{specs => data/ua}/os/netrange.json | 0 test/{specs => data/ua}/os/nettv.json | 0 test/{specs => data/ua}/os/nintendo.json | 0 test/{specs => data/ua}/os/openbsd.json | 0 test/{specs => data/ua}/os/openharmony.json | 0 test/{specs => data/ua}/os/os2.json | 0 test/{specs => data/ua}/os/palm.json | 0 test/{specs => data/ua}/os/pclinuxos.json | 0 test/{specs => data/ua}/os/pico.json | 0 test/{specs => data/ua}/os/plan9.json | 0 test/{specs => data/ua}/os/playstation.json | 0 test/{specs => data/ua}/os/qnx.json | 0 test/{specs => data/ua}/os/raspbian.json | 0 test/{specs => data/ua}/os/redhat.json | 0 test/{specs => data/ua}/os/rim-tablet-os.json | 0 test/{specs => data/ua}/os/risc-os.json | 0 test/{specs => data/ua}/os/sabayon.json | 0 test/{specs => data/ua}/os/sailfish.json | 0 test/{specs => data/ua}/os/serenityos.json | 0 test/{specs => data/ua}/os/series40.json | 0 test/{specs => data/ua}/os/slackware.json | 0 test/{specs => data/ua}/os/solaris.json | 0 test/{specs => data/ua}/os/suse.json | 0 test/{specs => data/ua}/os/symbian.json | 0 test/{specs => data/ua}/os/tizen.json | 0 test/{specs => data/ua}/os/ubuntu.json | 0 test/{specs => data/ua}/os/unix.json | 0 test/{specs => data/ua}/os/watchos.json | 0 test/{specs => data/ua}/os/webos.json | 0 test/{specs => data/ua}/os/windows-mobile.json | 0 test/{specs => data/ua}/os/windows-phone.json | 0 test/{specs => data/ua}/os/windows.json | 0 test/{specs => data/ua}/os/xbox.json | 0 test/{specs => data/ua}/os/zenwalk.json | 0 .../browser.spec.mjs} | 11 ++++++++--- test/{jazzer-fuzz-test.js => fuzz/redos.js} | 4 ++-- test/{dts-test.ts => static/dts-lint.ts} | 4 ++-- test/{mocha-test-es6.mjs => unit/es6.mjs} | 4 ++-- .../extensions.js} | 14 +++++++------- test/{mocha-test-helpers.js => unit/helpers.js} | 6 +++--- test/{mocha-test.js => unit/main.js} | 12 ++++++------ 149 files changed, 34 insertions(+), 29 deletions(-) rename test/{specs => data/ua}/browser/browser-all.json (100%) rename test/{specs => data/ua}/cpu/cpu-all.json (100%) rename test/{specs => data/ua}/device/_others.json (100%) rename test/{specs => data/ua}/device/acer.json (100%) rename test/{specs => data/ua}/device/advan.json (100%) rename test/{specs => data/ua}/device/alcatel.json (100%) rename test/{specs => data/ua}/device/amazon.json (100%) rename test/{specs => data/ua}/device/apple.json (100%) rename test/{specs => data/ua}/device/asus.json (100%) rename test/{specs => data/ua}/device/blackberry.json (100%) rename test/{specs => data/ua}/device/cat.json (100%) rename test/{specs => data/ua}/device/energizer.json (100%) rename test/{specs => data/ua}/device/facebook.json (100%) rename test/{specs => data/ua}/device/fairphone.json (100%) rename test/{specs => data/ua}/device/google.json (100%) rename test/{specs => data/ua}/device/hmd.json (100%) rename test/{specs => data/ua}/device/honor.json (100%) rename test/{specs => data/ua}/device/htc.json (100%) rename test/{specs => data/ua}/device/huawei.json (100%) rename test/{specs => data/ua}/device/imo.json (100%) rename test/{specs => data/ua}/device/infinix.json (100%) rename test/{specs => data/ua}/device/itel.json (100%) rename test/{specs => data/ua}/device/jolla.json (100%) rename test/{specs => data/ua}/device/kobo.json (100%) rename test/{specs => data/ua}/device/lenovo.json (100%) rename test/{specs => data/ua}/device/lg.json (100%) rename test/{specs => data/ua}/device/meizu.json (100%) rename test/{specs => data/ua}/device/micromax.json (100%) rename test/{specs => data/ua}/device/microsoft.json (100%) rename test/{specs => data/ua}/device/motorola.json (100%) rename test/{specs => data/ua}/device/nintendo.json (100%) rename test/{specs => data/ua}/device/nokia.json (100%) rename test/{specs => data/ua}/device/nothing.json (100%) rename test/{specs => data/ua}/device/nvidia.json (100%) rename test/{specs => data/ua}/device/oneplus.json (100%) rename test/{specs => data/ua}/device/oppo.json (100%) rename test/{specs => data/ua}/device/ouya.json (100%) rename test/{specs => data/ua}/device/panasonic.json (100%) rename test/{specs => data/ua}/device/pico.json (100%) rename test/{specs => data/ua}/device/polytron.json (100%) rename test/{specs => data/ua}/device/realme.json (100%) rename test/{specs => data/ua}/device/roku.json (100%) rename test/{specs => data/ua}/device/samsung.json (100%) rename test/{specs => data/ua}/device/sharp.json (100%) rename test/{specs => data/ua}/device/smartfren.json (100%) rename test/{specs => data/ua}/device/sony.json (100%) rename test/{specs => data/ua}/device/tcl.json (100%) rename test/{specs => data/ua}/device/technisat.json (100%) rename test/{specs => data/ua}/device/tecno.json (100%) rename test/{specs => data/ua}/device/tesla.json (100%) rename test/{specs => data/ua}/device/ulefone.json (100%) rename test/{specs => data/ua}/device/vivo.json (100%) rename test/{specs => data/ua}/device/xiaomi.json (100%) rename test/{specs => data/ua}/device/zte.json (100%) rename test/{specs => data/ua}/engine/engine-all.json (100%) rename test/{specs => data/ua}/extension/cli.json (100%) rename test/{specs => data/ua}/extension/crawler.json (100%) rename test/{specs => data/ua}/extension/email.json (100%) rename test/{specs => data/ua}/extension/extra-devices.json (100%) rename test/{specs => data/ua}/extension/fetcher.json (100%) rename test/{specs => data/ua}/extension/library.json (100%) rename test/{specs => data/ua}/extension/mediaplayer.json (100%) rename test/{specs => data/ua}/os/aix.json (100%) rename test/{specs => data/ua}/os/amigaos.json (100%) rename test/{specs => data/ua}/os/android-x86.json (100%) rename test/{specs => data/ua}/os/android.json (100%) rename test/{specs => data/ua}/os/arch.json (100%) rename test/{specs => data/ua}/os/bada.json (100%) rename test/{specs => data/ua}/os/beos.json (100%) rename test/{specs => data/ua}/os/blackberry.json (100%) rename test/{specs => data/ua}/os/centos.json (100%) rename test/{specs => data/ua}/os/chrome-os.json (100%) rename test/{specs => data/ua}/os/chromecast-android.json (100%) rename test/{specs => data/ua}/os/chromecast-fuchsia.json (100%) rename test/{specs => data/ua}/os/chromecast-linux.json (100%) rename test/{specs => data/ua}/os/chromecast-smartspeaker.json (100%) rename test/{specs => data/ua}/os/contiki.json (100%) rename test/{specs => data/ua}/os/debian.json (100%) rename test/{specs => data/ua}/os/deepin.json (100%) rename test/{specs => data/ua}/os/dragonfly.json (100%) rename test/{specs => data/ua}/os/elementary-os.json (100%) rename test/{specs => data/ua}/os/fedora.json (100%) rename test/{specs => data/ua}/os/firefox-os.json (100%) rename test/{specs => data/ua}/os/freebsd.json (100%) rename test/{specs => data/ua}/os/fuchsia.json (100%) rename test/{specs => data/ua}/os/gentoo.json (100%) rename test/{specs => data/ua}/os/ghostbsd.json (100%) rename test/{specs => data/ua}/os/haiku.json (100%) rename test/{specs => data/ua}/os/harmonyos.json (100%) rename test/{specs => data/ua}/os/hp-ux.json (100%) rename test/{specs => data/ua}/os/hurd.json (100%) rename test/{specs => data/ua}/os/ios.json (100%) rename test/{specs => data/ua}/os/joli.json (100%) rename test/{specs => data/ua}/os/kaios.json (100%) rename test/{specs => data/ua}/os/kubuntu.json (100%) rename test/{specs => data/ua}/os/linpus.json (100%) rename test/{specs => data/ua}/os/linspire.json (100%) rename test/{specs => data/ua}/os/linux.json (100%) rename test/{specs => data/ua}/os/macos.json (100%) rename test/{specs => data/ua}/os/maemo.json (100%) rename test/{specs => data/ua}/os/mandriva.json (100%) rename test/{specs => data/ua}/os/manjaro.json (100%) rename test/{specs => data/ua}/os/meego.json (100%) rename test/{specs => data/ua}/os/minix.json (100%) rename test/{specs => data/ua}/os/mint.json (100%) rename test/{specs => data/ua}/os/morphos.json (100%) rename test/{specs => data/ua}/os/netbsd.json (100%) rename test/{specs => data/ua}/os/netrange.json (100%) rename test/{specs => data/ua}/os/nettv.json (100%) rename test/{specs => data/ua}/os/nintendo.json (100%) rename test/{specs => data/ua}/os/openbsd.json (100%) rename test/{specs => data/ua}/os/openharmony.json (100%) rename test/{specs => data/ua}/os/os2.json (100%) rename test/{specs => data/ua}/os/palm.json (100%) rename test/{specs => data/ua}/os/pclinuxos.json (100%) rename test/{specs => data/ua}/os/pico.json (100%) rename test/{specs => data/ua}/os/plan9.json (100%) rename test/{specs => data/ua}/os/playstation.json (100%) rename test/{specs => data/ua}/os/qnx.json (100%) rename test/{specs => data/ua}/os/raspbian.json (100%) rename test/{specs => data/ua}/os/redhat.json (100%) rename test/{specs => data/ua}/os/rim-tablet-os.json (100%) rename test/{specs => data/ua}/os/risc-os.json (100%) rename test/{specs => data/ua}/os/sabayon.json (100%) rename test/{specs => data/ua}/os/sailfish.json (100%) rename test/{specs => data/ua}/os/serenityos.json (100%) rename test/{specs => data/ua}/os/series40.json (100%) rename test/{specs => data/ua}/os/slackware.json (100%) rename test/{specs => data/ua}/os/solaris.json (100%) rename test/{specs => data/ua}/os/suse.json (100%) rename test/{specs => data/ua}/os/symbian.json (100%) rename test/{specs => data/ua}/os/tizen.json (100%) rename test/{specs => data/ua}/os/ubuntu.json (100%) rename test/{specs => data/ua}/os/unix.json (100%) rename test/{specs => data/ua}/os/watchos.json (100%) rename test/{specs => data/ua}/os/webos.json (100%) rename test/{specs => data/ua}/os/windows-mobile.json (100%) rename test/{specs => data/ua}/os/windows-phone.json (100%) rename test/{specs => data/ua}/os/windows.json (100%) rename test/{specs => data/ua}/os/xbox.json (100%) rename test/{specs => data/ua}/os/zenwalk.json (100%) rename test/{playwright-test-main.spec.mjs => e2e/browser.spec.mjs} (97%) rename test/{jazzer-fuzz-test.js => fuzz/redos.js} (87%) rename test/{dts-test.ts => static/dts-lint.ts} (93%) rename test/{mocha-test-es6.mjs => unit/es6.mjs} (88%) rename test/{mocha-test-extension.js => unit/extensions.js} (91%) rename test/{mocha-test-helpers.js => unit/helpers.js} (94%) rename test/{mocha-test.js => unit/main.js} (98%) diff --git a/package.json b/package.json index aa887d2b9..b9d958133 100755 --- a/package.json +++ b/package.json @@ -202,14 +202,14 @@ "scripts": { "build": "./script/build-dist.sh && ./script/build-esm.js", "build+test": "npm run build && npm run test", - "fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync", + "fuzz": "jazzer ./test/fuzz/redos.js --sync", "test": "./script/test-all.sh", - "test:dts-lint": "tsd --typings src/main/ua-parser.d.ts --files test/dts-test.ts", + "test:dts-lint": "tsd --typings src/main/ua-parser.d.ts --files test/static/dts-lint.ts", "test:eslint": "eslint src && eslint script", "test:jshint": "jshint src/main", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", - "test:mocha": "mocha test/mocha*js", - "test:playwright": "playwright test" + "test:mocha": "mocha test/unit", + "test:playwright": "playwright test test/e2e --browser all" }, "dependencies": { "detect-europe-js": "^0.1.2", diff --git a/test/specs/browser/browser-all.json b/test/data/ua/browser/browser-all.json similarity index 100% rename from test/specs/browser/browser-all.json rename to test/data/ua/browser/browser-all.json diff --git a/test/specs/cpu/cpu-all.json b/test/data/ua/cpu/cpu-all.json similarity index 100% rename from test/specs/cpu/cpu-all.json rename to test/data/ua/cpu/cpu-all.json diff --git a/test/specs/device/_others.json b/test/data/ua/device/_others.json similarity index 100% rename from test/specs/device/_others.json rename to test/data/ua/device/_others.json diff --git a/test/specs/device/acer.json b/test/data/ua/device/acer.json similarity index 100% rename from test/specs/device/acer.json rename to test/data/ua/device/acer.json diff --git a/test/specs/device/advan.json b/test/data/ua/device/advan.json similarity index 100% rename from test/specs/device/advan.json rename to test/data/ua/device/advan.json diff --git a/test/specs/device/alcatel.json b/test/data/ua/device/alcatel.json similarity index 100% rename from test/specs/device/alcatel.json rename to test/data/ua/device/alcatel.json diff --git a/test/specs/device/amazon.json b/test/data/ua/device/amazon.json similarity index 100% rename from test/specs/device/amazon.json rename to test/data/ua/device/amazon.json diff --git a/test/specs/device/apple.json b/test/data/ua/device/apple.json similarity index 100% rename from test/specs/device/apple.json rename to test/data/ua/device/apple.json diff --git a/test/specs/device/asus.json b/test/data/ua/device/asus.json similarity index 100% rename from test/specs/device/asus.json rename to test/data/ua/device/asus.json diff --git a/test/specs/device/blackberry.json b/test/data/ua/device/blackberry.json similarity index 100% rename from test/specs/device/blackberry.json rename to test/data/ua/device/blackberry.json diff --git a/test/specs/device/cat.json b/test/data/ua/device/cat.json similarity index 100% rename from test/specs/device/cat.json rename to test/data/ua/device/cat.json diff --git a/test/specs/device/energizer.json b/test/data/ua/device/energizer.json similarity index 100% rename from test/specs/device/energizer.json rename to test/data/ua/device/energizer.json diff --git a/test/specs/device/facebook.json b/test/data/ua/device/facebook.json similarity index 100% rename from test/specs/device/facebook.json rename to test/data/ua/device/facebook.json diff --git a/test/specs/device/fairphone.json b/test/data/ua/device/fairphone.json similarity index 100% rename from test/specs/device/fairphone.json rename to test/data/ua/device/fairphone.json diff --git a/test/specs/device/google.json b/test/data/ua/device/google.json similarity index 100% rename from test/specs/device/google.json rename to test/data/ua/device/google.json diff --git a/test/specs/device/hmd.json b/test/data/ua/device/hmd.json similarity index 100% rename from test/specs/device/hmd.json rename to test/data/ua/device/hmd.json diff --git a/test/specs/device/honor.json b/test/data/ua/device/honor.json similarity index 100% rename from test/specs/device/honor.json rename to test/data/ua/device/honor.json diff --git a/test/specs/device/htc.json b/test/data/ua/device/htc.json similarity index 100% rename from test/specs/device/htc.json rename to test/data/ua/device/htc.json diff --git a/test/specs/device/huawei.json b/test/data/ua/device/huawei.json similarity index 100% rename from test/specs/device/huawei.json rename to test/data/ua/device/huawei.json diff --git a/test/specs/device/imo.json b/test/data/ua/device/imo.json similarity index 100% rename from test/specs/device/imo.json rename to test/data/ua/device/imo.json diff --git a/test/specs/device/infinix.json b/test/data/ua/device/infinix.json similarity index 100% rename from test/specs/device/infinix.json rename to test/data/ua/device/infinix.json diff --git a/test/specs/device/itel.json b/test/data/ua/device/itel.json similarity index 100% rename from test/specs/device/itel.json rename to test/data/ua/device/itel.json diff --git a/test/specs/device/jolla.json b/test/data/ua/device/jolla.json similarity index 100% rename from test/specs/device/jolla.json rename to test/data/ua/device/jolla.json diff --git a/test/specs/device/kobo.json b/test/data/ua/device/kobo.json similarity index 100% rename from test/specs/device/kobo.json rename to test/data/ua/device/kobo.json diff --git a/test/specs/device/lenovo.json b/test/data/ua/device/lenovo.json similarity index 100% rename from test/specs/device/lenovo.json rename to test/data/ua/device/lenovo.json diff --git a/test/specs/device/lg.json b/test/data/ua/device/lg.json similarity index 100% rename from test/specs/device/lg.json rename to test/data/ua/device/lg.json diff --git a/test/specs/device/meizu.json b/test/data/ua/device/meizu.json similarity index 100% rename from test/specs/device/meizu.json rename to test/data/ua/device/meizu.json diff --git a/test/specs/device/micromax.json b/test/data/ua/device/micromax.json similarity index 100% rename from test/specs/device/micromax.json rename to test/data/ua/device/micromax.json diff --git a/test/specs/device/microsoft.json b/test/data/ua/device/microsoft.json similarity index 100% rename from test/specs/device/microsoft.json rename to test/data/ua/device/microsoft.json diff --git a/test/specs/device/motorola.json b/test/data/ua/device/motorola.json similarity index 100% rename from test/specs/device/motorola.json rename to test/data/ua/device/motorola.json diff --git a/test/specs/device/nintendo.json b/test/data/ua/device/nintendo.json similarity index 100% rename from test/specs/device/nintendo.json rename to test/data/ua/device/nintendo.json diff --git a/test/specs/device/nokia.json b/test/data/ua/device/nokia.json similarity index 100% rename from test/specs/device/nokia.json rename to test/data/ua/device/nokia.json diff --git a/test/specs/device/nothing.json b/test/data/ua/device/nothing.json similarity index 100% rename from test/specs/device/nothing.json rename to test/data/ua/device/nothing.json diff --git a/test/specs/device/nvidia.json b/test/data/ua/device/nvidia.json similarity index 100% rename from test/specs/device/nvidia.json rename to test/data/ua/device/nvidia.json diff --git a/test/specs/device/oneplus.json b/test/data/ua/device/oneplus.json similarity index 100% rename from test/specs/device/oneplus.json rename to test/data/ua/device/oneplus.json diff --git a/test/specs/device/oppo.json b/test/data/ua/device/oppo.json similarity index 100% rename from test/specs/device/oppo.json rename to test/data/ua/device/oppo.json diff --git a/test/specs/device/ouya.json b/test/data/ua/device/ouya.json similarity index 100% rename from test/specs/device/ouya.json rename to test/data/ua/device/ouya.json diff --git a/test/specs/device/panasonic.json b/test/data/ua/device/panasonic.json similarity index 100% rename from test/specs/device/panasonic.json rename to test/data/ua/device/panasonic.json diff --git a/test/specs/device/pico.json b/test/data/ua/device/pico.json similarity index 100% rename from test/specs/device/pico.json rename to test/data/ua/device/pico.json diff --git a/test/specs/device/polytron.json b/test/data/ua/device/polytron.json similarity index 100% rename from test/specs/device/polytron.json rename to test/data/ua/device/polytron.json diff --git a/test/specs/device/realme.json b/test/data/ua/device/realme.json similarity index 100% rename from test/specs/device/realme.json rename to test/data/ua/device/realme.json diff --git a/test/specs/device/roku.json b/test/data/ua/device/roku.json similarity index 100% rename from test/specs/device/roku.json rename to test/data/ua/device/roku.json diff --git a/test/specs/device/samsung.json b/test/data/ua/device/samsung.json similarity index 100% rename from test/specs/device/samsung.json rename to test/data/ua/device/samsung.json diff --git a/test/specs/device/sharp.json b/test/data/ua/device/sharp.json similarity index 100% rename from test/specs/device/sharp.json rename to test/data/ua/device/sharp.json diff --git a/test/specs/device/smartfren.json b/test/data/ua/device/smartfren.json similarity index 100% rename from test/specs/device/smartfren.json rename to test/data/ua/device/smartfren.json diff --git a/test/specs/device/sony.json b/test/data/ua/device/sony.json similarity index 100% rename from test/specs/device/sony.json rename to test/data/ua/device/sony.json diff --git a/test/specs/device/tcl.json b/test/data/ua/device/tcl.json similarity index 100% rename from test/specs/device/tcl.json rename to test/data/ua/device/tcl.json diff --git a/test/specs/device/technisat.json b/test/data/ua/device/technisat.json similarity index 100% rename from test/specs/device/technisat.json rename to test/data/ua/device/technisat.json diff --git a/test/specs/device/tecno.json b/test/data/ua/device/tecno.json similarity index 100% rename from test/specs/device/tecno.json rename to test/data/ua/device/tecno.json diff --git a/test/specs/device/tesla.json b/test/data/ua/device/tesla.json similarity index 100% rename from test/specs/device/tesla.json rename to test/data/ua/device/tesla.json diff --git a/test/specs/device/ulefone.json b/test/data/ua/device/ulefone.json similarity index 100% rename from test/specs/device/ulefone.json rename to test/data/ua/device/ulefone.json diff --git a/test/specs/device/vivo.json b/test/data/ua/device/vivo.json similarity index 100% rename from test/specs/device/vivo.json rename to test/data/ua/device/vivo.json diff --git a/test/specs/device/xiaomi.json b/test/data/ua/device/xiaomi.json similarity index 100% rename from test/specs/device/xiaomi.json rename to test/data/ua/device/xiaomi.json diff --git a/test/specs/device/zte.json b/test/data/ua/device/zte.json similarity index 100% rename from test/specs/device/zte.json rename to test/data/ua/device/zte.json diff --git a/test/specs/engine/engine-all.json b/test/data/ua/engine/engine-all.json similarity index 100% rename from test/specs/engine/engine-all.json rename to test/data/ua/engine/engine-all.json diff --git a/test/specs/extension/cli.json b/test/data/ua/extension/cli.json similarity index 100% rename from test/specs/extension/cli.json rename to test/data/ua/extension/cli.json diff --git a/test/specs/extension/crawler.json b/test/data/ua/extension/crawler.json similarity index 100% rename from test/specs/extension/crawler.json rename to test/data/ua/extension/crawler.json diff --git a/test/specs/extension/email.json b/test/data/ua/extension/email.json similarity index 100% rename from test/specs/extension/email.json rename to test/data/ua/extension/email.json diff --git a/test/specs/extension/extra-devices.json b/test/data/ua/extension/extra-devices.json similarity index 100% rename from test/specs/extension/extra-devices.json rename to test/data/ua/extension/extra-devices.json diff --git a/test/specs/extension/fetcher.json b/test/data/ua/extension/fetcher.json similarity index 100% rename from test/specs/extension/fetcher.json rename to test/data/ua/extension/fetcher.json diff --git a/test/specs/extension/library.json b/test/data/ua/extension/library.json similarity index 100% rename from test/specs/extension/library.json rename to test/data/ua/extension/library.json diff --git a/test/specs/extension/mediaplayer.json b/test/data/ua/extension/mediaplayer.json similarity index 100% rename from test/specs/extension/mediaplayer.json rename to test/data/ua/extension/mediaplayer.json diff --git a/test/specs/os/aix.json b/test/data/ua/os/aix.json similarity index 100% rename from test/specs/os/aix.json rename to test/data/ua/os/aix.json diff --git a/test/specs/os/amigaos.json b/test/data/ua/os/amigaos.json similarity index 100% rename from test/specs/os/amigaos.json rename to test/data/ua/os/amigaos.json diff --git a/test/specs/os/android-x86.json b/test/data/ua/os/android-x86.json similarity index 100% rename from test/specs/os/android-x86.json rename to test/data/ua/os/android-x86.json diff --git a/test/specs/os/android.json b/test/data/ua/os/android.json similarity index 100% rename from test/specs/os/android.json rename to test/data/ua/os/android.json diff --git a/test/specs/os/arch.json b/test/data/ua/os/arch.json similarity index 100% rename from test/specs/os/arch.json rename to test/data/ua/os/arch.json diff --git a/test/specs/os/bada.json b/test/data/ua/os/bada.json similarity index 100% rename from test/specs/os/bada.json rename to test/data/ua/os/bada.json diff --git a/test/specs/os/beos.json b/test/data/ua/os/beos.json similarity index 100% rename from test/specs/os/beos.json rename to test/data/ua/os/beos.json diff --git a/test/specs/os/blackberry.json b/test/data/ua/os/blackberry.json similarity index 100% rename from test/specs/os/blackberry.json rename to test/data/ua/os/blackberry.json diff --git a/test/specs/os/centos.json b/test/data/ua/os/centos.json similarity index 100% rename from test/specs/os/centos.json rename to test/data/ua/os/centos.json diff --git a/test/specs/os/chrome-os.json b/test/data/ua/os/chrome-os.json similarity index 100% rename from test/specs/os/chrome-os.json rename to test/data/ua/os/chrome-os.json diff --git a/test/specs/os/chromecast-android.json b/test/data/ua/os/chromecast-android.json similarity index 100% rename from test/specs/os/chromecast-android.json rename to test/data/ua/os/chromecast-android.json diff --git a/test/specs/os/chromecast-fuchsia.json b/test/data/ua/os/chromecast-fuchsia.json similarity index 100% rename from test/specs/os/chromecast-fuchsia.json rename to test/data/ua/os/chromecast-fuchsia.json diff --git a/test/specs/os/chromecast-linux.json b/test/data/ua/os/chromecast-linux.json similarity index 100% rename from test/specs/os/chromecast-linux.json rename to test/data/ua/os/chromecast-linux.json diff --git a/test/specs/os/chromecast-smartspeaker.json b/test/data/ua/os/chromecast-smartspeaker.json similarity index 100% rename from test/specs/os/chromecast-smartspeaker.json rename to test/data/ua/os/chromecast-smartspeaker.json diff --git a/test/specs/os/contiki.json b/test/data/ua/os/contiki.json similarity index 100% rename from test/specs/os/contiki.json rename to test/data/ua/os/contiki.json diff --git a/test/specs/os/debian.json b/test/data/ua/os/debian.json similarity index 100% rename from test/specs/os/debian.json rename to test/data/ua/os/debian.json diff --git a/test/specs/os/deepin.json b/test/data/ua/os/deepin.json similarity index 100% rename from test/specs/os/deepin.json rename to test/data/ua/os/deepin.json diff --git a/test/specs/os/dragonfly.json b/test/data/ua/os/dragonfly.json similarity index 100% rename from test/specs/os/dragonfly.json rename to test/data/ua/os/dragonfly.json diff --git a/test/specs/os/elementary-os.json b/test/data/ua/os/elementary-os.json similarity index 100% rename from test/specs/os/elementary-os.json rename to test/data/ua/os/elementary-os.json diff --git a/test/specs/os/fedora.json b/test/data/ua/os/fedora.json similarity index 100% rename from test/specs/os/fedora.json rename to test/data/ua/os/fedora.json diff --git a/test/specs/os/firefox-os.json b/test/data/ua/os/firefox-os.json similarity index 100% rename from test/specs/os/firefox-os.json rename to test/data/ua/os/firefox-os.json diff --git a/test/specs/os/freebsd.json b/test/data/ua/os/freebsd.json similarity index 100% rename from test/specs/os/freebsd.json rename to test/data/ua/os/freebsd.json diff --git a/test/specs/os/fuchsia.json b/test/data/ua/os/fuchsia.json similarity index 100% rename from test/specs/os/fuchsia.json rename to test/data/ua/os/fuchsia.json diff --git a/test/specs/os/gentoo.json b/test/data/ua/os/gentoo.json similarity index 100% rename from test/specs/os/gentoo.json rename to test/data/ua/os/gentoo.json diff --git a/test/specs/os/ghostbsd.json b/test/data/ua/os/ghostbsd.json similarity index 100% rename from test/specs/os/ghostbsd.json rename to test/data/ua/os/ghostbsd.json diff --git a/test/specs/os/haiku.json b/test/data/ua/os/haiku.json similarity index 100% rename from test/specs/os/haiku.json rename to test/data/ua/os/haiku.json diff --git a/test/specs/os/harmonyos.json b/test/data/ua/os/harmonyos.json similarity index 100% rename from test/specs/os/harmonyos.json rename to test/data/ua/os/harmonyos.json diff --git a/test/specs/os/hp-ux.json b/test/data/ua/os/hp-ux.json similarity index 100% rename from test/specs/os/hp-ux.json rename to test/data/ua/os/hp-ux.json diff --git a/test/specs/os/hurd.json b/test/data/ua/os/hurd.json similarity index 100% rename from test/specs/os/hurd.json rename to test/data/ua/os/hurd.json diff --git a/test/specs/os/ios.json b/test/data/ua/os/ios.json similarity index 100% rename from test/specs/os/ios.json rename to test/data/ua/os/ios.json diff --git a/test/specs/os/joli.json b/test/data/ua/os/joli.json similarity index 100% rename from test/specs/os/joli.json rename to test/data/ua/os/joli.json diff --git a/test/specs/os/kaios.json b/test/data/ua/os/kaios.json similarity index 100% rename from test/specs/os/kaios.json rename to test/data/ua/os/kaios.json diff --git a/test/specs/os/kubuntu.json b/test/data/ua/os/kubuntu.json similarity index 100% rename from test/specs/os/kubuntu.json rename to test/data/ua/os/kubuntu.json diff --git a/test/specs/os/linpus.json b/test/data/ua/os/linpus.json similarity index 100% rename from test/specs/os/linpus.json rename to test/data/ua/os/linpus.json diff --git a/test/specs/os/linspire.json b/test/data/ua/os/linspire.json similarity index 100% rename from test/specs/os/linspire.json rename to test/data/ua/os/linspire.json diff --git a/test/specs/os/linux.json b/test/data/ua/os/linux.json similarity index 100% rename from test/specs/os/linux.json rename to test/data/ua/os/linux.json diff --git a/test/specs/os/macos.json b/test/data/ua/os/macos.json similarity index 100% rename from test/specs/os/macos.json rename to test/data/ua/os/macos.json diff --git a/test/specs/os/maemo.json b/test/data/ua/os/maemo.json similarity index 100% rename from test/specs/os/maemo.json rename to test/data/ua/os/maemo.json diff --git a/test/specs/os/mandriva.json b/test/data/ua/os/mandriva.json similarity index 100% rename from test/specs/os/mandriva.json rename to test/data/ua/os/mandriva.json diff --git a/test/specs/os/manjaro.json b/test/data/ua/os/manjaro.json similarity index 100% rename from test/specs/os/manjaro.json rename to test/data/ua/os/manjaro.json diff --git a/test/specs/os/meego.json b/test/data/ua/os/meego.json similarity index 100% rename from test/specs/os/meego.json rename to test/data/ua/os/meego.json diff --git a/test/specs/os/minix.json b/test/data/ua/os/minix.json similarity index 100% rename from test/specs/os/minix.json rename to test/data/ua/os/minix.json diff --git a/test/specs/os/mint.json b/test/data/ua/os/mint.json similarity index 100% rename from test/specs/os/mint.json rename to test/data/ua/os/mint.json diff --git a/test/specs/os/morphos.json b/test/data/ua/os/morphos.json similarity index 100% rename from test/specs/os/morphos.json rename to test/data/ua/os/morphos.json diff --git a/test/specs/os/netbsd.json b/test/data/ua/os/netbsd.json similarity index 100% rename from test/specs/os/netbsd.json rename to test/data/ua/os/netbsd.json diff --git a/test/specs/os/netrange.json b/test/data/ua/os/netrange.json similarity index 100% rename from test/specs/os/netrange.json rename to test/data/ua/os/netrange.json diff --git a/test/specs/os/nettv.json b/test/data/ua/os/nettv.json similarity index 100% rename from test/specs/os/nettv.json rename to test/data/ua/os/nettv.json diff --git a/test/specs/os/nintendo.json b/test/data/ua/os/nintendo.json similarity index 100% rename from test/specs/os/nintendo.json rename to test/data/ua/os/nintendo.json diff --git a/test/specs/os/openbsd.json b/test/data/ua/os/openbsd.json similarity index 100% rename from test/specs/os/openbsd.json rename to test/data/ua/os/openbsd.json diff --git a/test/specs/os/openharmony.json b/test/data/ua/os/openharmony.json similarity index 100% rename from test/specs/os/openharmony.json rename to test/data/ua/os/openharmony.json diff --git a/test/specs/os/os2.json b/test/data/ua/os/os2.json similarity index 100% rename from test/specs/os/os2.json rename to test/data/ua/os/os2.json diff --git a/test/specs/os/palm.json b/test/data/ua/os/palm.json similarity index 100% rename from test/specs/os/palm.json rename to test/data/ua/os/palm.json diff --git a/test/specs/os/pclinuxos.json b/test/data/ua/os/pclinuxos.json similarity index 100% rename from test/specs/os/pclinuxos.json rename to test/data/ua/os/pclinuxos.json diff --git a/test/specs/os/pico.json b/test/data/ua/os/pico.json similarity index 100% rename from test/specs/os/pico.json rename to test/data/ua/os/pico.json diff --git a/test/specs/os/plan9.json b/test/data/ua/os/plan9.json similarity index 100% rename from test/specs/os/plan9.json rename to test/data/ua/os/plan9.json diff --git a/test/specs/os/playstation.json b/test/data/ua/os/playstation.json similarity index 100% rename from test/specs/os/playstation.json rename to test/data/ua/os/playstation.json diff --git a/test/specs/os/qnx.json b/test/data/ua/os/qnx.json similarity index 100% rename from test/specs/os/qnx.json rename to test/data/ua/os/qnx.json diff --git a/test/specs/os/raspbian.json b/test/data/ua/os/raspbian.json similarity index 100% rename from test/specs/os/raspbian.json rename to test/data/ua/os/raspbian.json diff --git a/test/specs/os/redhat.json b/test/data/ua/os/redhat.json similarity index 100% rename from test/specs/os/redhat.json rename to test/data/ua/os/redhat.json diff --git a/test/specs/os/rim-tablet-os.json b/test/data/ua/os/rim-tablet-os.json similarity index 100% rename from test/specs/os/rim-tablet-os.json rename to test/data/ua/os/rim-tablet-os.json diff --git a/test/specs/os/risc-os.json b/test/data/ua/os/risc-os.json similarity index 100% rename from test/specs/os/risc-os.json rename to test/data/ua/os/risc-os.json diff --git a/test/specs/os/sabayon.json b/test/data/ua/os/sabayon.json similarity index 100% rename from test/specs/os/sabayon.json rename to test/data/ua/os/sabayon.json diff --git a/test/specs/os/sailfish.json b/test/data/ua/os/sailfish.json similarity index 100% rename from test/specs/os/sailfish.json rename to test/data/ua/os/sailfish.json diff --git a/test/specs/os/serenityos.json b/test/data/ua/os/serenityos.json similarity index 100% rename from test/specs/os/serenityos.json rename to test/data/ua/os/serenityos.json diff --git a/test/specs/os/series40.json b/test/data/ua/os/series40.json similarity index 100% rename from test/specs/os/series40.json rename to test/data/ua/os/series40.json diff --git a/test/specs/os/slackware.json b/test/data/ua/os/slackware.json similarity index 100% rename from test/specs/os/slackware.json rename to test/data/ua/os/slackware.json diff --git a/test/specs/os/solaris.json b/test/data/ua/os/solaris.json similarity index 100% rename from test/specs/os/solaris.json rename to test/data/ua/os/solaris.json diff --git a/test/specs/os/suse.json b/test/data/ua/os/suse.json similarity index 100% rename from test/specs/os/suse.json rename to test/data/ua/os/suse.json diff --git a/test/specs/os/symbian.json b/test/data/ua/os/symbian.json similarity index 100% rename from test/specs/os/symbian.json rename to test/data/ua/os/symbian.json diff --git a/test/specs/os/tizen.json b/test/data/ua/os/tizen.json similarity index 100% rename from test/specs/os/tizen.json rename to test/data/ua/os/tizen.json diff --git a/test/specs/os/ubuntu.json b/test/data/ua/os/ubuntu.json similarity index 100% rename from test/specs/os/ubuntu.json rename to test/data/ua/os/ubuntu.json diff --git a/test/specs/os/unix.json b/test/data/ua/os/unix.json similarity index 100% rename from test/specs/os/unix.json rename to test/data/ua/os/unix.json diff --git a/test/specs/os/watchos.json b/test/data/ua/os/watchos.json similarity index 100% rename from test/specs/os/watchos.json rename to test/data/ua/os/watchos.json diff --git a/test/specs/os/webos.json b/test/data/ua/os/webos.json similarity index 100% rename from test/specs/os/webos.json rename to test/data/ua/os/webos.json diff --git a/test/specs/os/windows-mobile.json b/test/data/ua/os/windows-mobile.json similarity index 100% rename from test/specs/os/windows-mobile.json rename to test/data/ua/os/windows-mobile.json diff --git a/test/specs/os/windows-phone.json b/test/data/ua/os/windows-phone.json similarity index 100% rename from test/specs/os/windows-phone.json rename to test/data/ua/os/windows-phone.json diff --git a/test/specs/os/windows.json b/test/data/ua/os/windows.json similarity index 100% rename from test/specs/os/windows.json rename to test/data/ua/os/windows.json diff --git a/test/specs/os/xbox.json b/test/data/ua/os/xbox.json similarity index 100% rename from test/specs/os/xbox.json rename to test/data/ua/os/xbox.json diff --git a/test/specs/os/zenwalk.json b/test/data/ua/os/zenwalk.json similarity index 100% rename from test/specs/os/zenwalk.json rename to test/data/ua/os/zenwalk.json diff --git a/test/playwright-test-main.spec.mjs b/test/e2e/browser.spec.mjs similarity index 97% rename from test/playwright-test-main.spec.mjs rename to test/e2e/browser.spec.mjs index db6e149f1..97431b65e 100644 --- a/test/playwright-test-main.spec.mjs +++ b/test/e2e/browser.spec.mjs @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'; import path from 'path'; import url from 'url'; -const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../')}/dist/ua-parser.html`; +const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../../')}/dist/ua-parser.html`; test.describe('Custom navigator.userAgent tests', () => { @@ -83,7 +83,7 @@ test.describe('withClientHints() tests', () => { test.describe('withFeatureCheck() tests', () => { - test('Detect Brave', async ({ page }) => { + test('Detect Brave', async ({ page, browserName }) => { await page.addInitScript(() => { Object.defineProperty(navigator, 'brave', { value: { @@ -94,7 +94,12 @@ test.describe('withFeatureCheck() tests', () => { await page.goto(localHtml); // @ts-ignore let uap = await page.evaluate(() => UAParser()); - expect(uap).toHaveProperty('browser.name', 'Chrome Headless'); + const browserMap = { + chromium: 'Chrome Headless', + firefox: 'Firefox', + webkit: 'Safari' + } + expect(uap).toHaveProperty('browser.name', browserMap[browserName]); // @ts-ignore uap = await page.evaluate(() => UAParser().withFeatureCheck()); expect(uap).toHaveProperty('browser.name', 'Brave'); diff --git a/test/jazzer-fuzz-test.js b/test/fuzz/redos.js similarity index 87% rename from test/jazzer-fuzz-test.js rename to test/fuzz/redos.js index 8d8704952..b04686218 100644 --- a/test/jazzer-fuzz-test.js +++ b/test/fuzz/redos.js @@ -1,6 +1,6 @@ const { FuzzedDataProvider } = require('@jazzer.js/core'); -const { UAParser } = require('../src/main/ua-parser'); -const UA_MAX_LENGTH = 350; +const { UAParser } = require('../../dist/main'); +const UA_MAX_LENGTH = 500; module.exports.fuzz = function (buffer) { const data = new FuzzedDataProvider(buffer); diff --git a/test/dts-test.ts b/test/static/dts-lint.ts similarity index 93% rename from test/dts-test.ts rename to test/static/dts-lint.ts index 39bb4a30f..bcfc26287 100644 --- a/test/dts-test.ts +++ b/test/static/dts-lint.ts @@ -1,6 +1,6 @@ import { expectType } from 'tsd'; -import { UAParser, IResult, IBrowser, ICPU, IEngine, IDevice, IOS } from "../src/main/ua-parser"; -import { isAppleSilicon, isChromeFamily } from "../src/helpers/ua-parser-helpers"; +import { UAParser, IResult, IBrowser, ICPU, IEngine, IDevice, IOS } from "../../src/main/ua-parser"; +import { isAppleSilicon, isChromeFamily } from "../../src/helpers/ua-parser-helpers"; const uastring = 'Mozilla/5.0 (X11; MyCustomOS; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0'; const extensions = { diff --git a/test/mocha-test-es6.mjs b/test/unit/es6.mjs similarity index 88% rename from test/mocha-test-es6.mjs rename to test/unit/es6.mjs index 69a334e6b..8e26908d5 100644 --- a/test/mocha-test-es6.mjs +++ b/test/unit/es6.mjs @@ -1,5 +1,5 @@ -import { UAParser } from '../src/main/ua-parser.mjs'; -import { CPU, Device, Engine } from '../src/enums/ua-parser-enums.mjs'; +import { UAParser } from '../../src/main/ua-parser.mjs'; +import { CPU, Device, Engine } from '../../src/enums/ua-parser-enums.mjs'; import * as assert from 'assert'; describe('Returns', () => { diff --git a/test/mocha-test-extension.js b/test/unit/extensions.js similarity index 91% rename from test/mocha-test-extension.js rename to test/unit/extensions.js index c32c1b824..6fd755bb1 100644 --- a/test/mocha-test-extension.js +++ b/test/unit/extensions.js @@ -3,13 +3,13 @@ const assert = require('assert'); const parseJS = require('@babel/parser').parse; const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); -const { UAParser } = require('../src/main/ua-parser'); -const clis = require('./specs/extension/cli.json'); -const crawlers = require('./specs/extension/crawler.json'); -const emails = require('./specs/extension/email.json'); -const fetchers = require('./specs/extension/fetcher.json'); -const libraries = require('./specs/extension/library.json'); -const { Bots, CLIs, Crawlers, Emails, Fetchers, Libraries } = require('../src/extensions/ua-parser-extensions'); +const { UAParser } = require('../../src/main/ua-parser'); +const clis = require('../data/ua/extension/cli.json'); +const crawlers = require('../data/ua/extension/crawler.json'); +const emails = require('../data/ua/extension/email.json'); +const fetchers = require('../data/ua/extension/fetcher.json'); +const libraries = require('../data/ua/extension/library.json'); +const { Bots, CLIs, Crawlers, Emails, Fetchers, Libraries } = require('../../src/extensions/ua-parser-extensions'); describe('Extensions', () => { [ diff --git a/test/mocha-test-helpers.js b/test/unit/helpers.js similarity index 94% rename from test/mocha-test-helpers.js rename to test/unit/helpers.js index d6170bcb5..fcd667859 100644 --- a/test/mocha-test-helpers.js +++ b/test/unit/helpers.js @@ -1,7 +1,7 @@ const assert = require('assert'); -const { UAParser } = require('../src/main/ua-parser'); -const { getDeviceVendor, isAppleSilicon, isAIBot, isBot, isChromeFamily } = require('../src/helpers/ua-parser-helpers'); -const { Bots, Emails } = require('../src/extensions/ua-parser-extensions'); +const { UAParser } = require('../../src/main/ua-parser'); +const { getDeviceVendor, isAppleSilicon, isAIBot, isBot, isChromeFamily } = require('../../src/helpers/ua-parser-helpers'); +const { Bots, Emails } = require('../../src/extensions/ua-parser-extensions'); describe('getDeviceVendor', () => { it('Can guess the device vendor from a model name', () => { diff --git a/test/mocha-test.js b/test/unit/main.js similarity index 98% rename from test/mocha-test.js rename to test/unit/main.js index 9c50a8200..04338821c 100644 --- a/test/mocha-test.js +++ b/test/unit/main.js @@ -4,12 +4,12 @@ var assert = require('assert'); var requirejs = require('requirejs'); var parseJS = require('@babel/parser').parse; var traverse = require('@babel/traverse').default; -var {UAParser} = require('../src/main/ua-parser'); -var browsers = require('./specs/browser/browser-all.json'); -var cpus = require('./specs/cpu/cpu-all.json'); -var devices = readJsonFiles('test/specs/device'); -var engines = require('./specs/engine/engine-all.json'); -var os = readJsonFiles('test/specs/os'); +var {UAParser} = require('../../src/main/ua-parser'); +var browsers = require('../data/ua/browser/browser-all.json'); +var cpus = require('../data/ua/cpu/cpu-all.json'); +var devices = readJsonFiles('test/data/ua/device'); +var engines = require('../data/ua/engine/engine-all.json'); +var os = readJsonFiles('test/data/ua/os'); var { Headers } = require('node-fetch'); function readJsonFiles(dir) { From e013038643b5a2f7f249ed05bab089c249f707d5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 29 Nov 2024 20:44:37 +0700 Subject: [PATCH 294/388] Improve cpu detection for x86 --- src/main/ua-parser.js | 2 +- test/data/ua/cpu/cpu-all.json | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 701375b4b..1f4a5ef3c 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -478,7 +478,7 @@ ], [[ARCHITECTURE, 'amd64']], [ /(ia32(?=;))/i, // IA32 (quicktime) - /((?:i[346]|x)86)[;\)]/i // IA32 (x86) + /\b((?:i[346]|x)86)\b/i // IA32 (x86) ], [[ARCHITECTURE, 'ia32']], [ /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 diff --git a/test/data/ua/cpu/cpu-all.json b/test/data/ua/cpu/cpu-all.json index 41df5d0fb..d43fbc0d6 100644 --- a/test/data/ua/cpu/cpu-all.json +++ b/test/data/ua/cpu/cpu-all.json @@ -7,6 +7,14 @@ "architecture" : "ia32" } }, + { + "desc" : "i686", + "ua" : "Mozilla/5.0 (X11; U; CrOS i686 9.10.0; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.253.0 Safari/532.5", + "expect" : + { + "architecture" : "ia32" + } + }, { "desc" : "i386", "ua" : "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7) Gecko/20040628 Epiphany/1.2.6", From b2fc76fe990888e80da9d5fdfe76632417acd553 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Nov 2024 07:30:04 +0700 Subject: [PATCH 295/388] [test] remove the client hints check in firefox & safari --- test/e2e/browser.spec.mjs | 80 +++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/test/e2e/browser.spec.mjs b/test/e2e/browser.spec.mjs index 97431b65e..848694c5c 100644 --- a/test/e2e/browser.spec.mjs +++ b/test/e2e/browser.spec.mjs @@ -3,6 +3,11 @@ import path from 'path'; import url from 'url'; const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../../')}/dist/ua-parser.html`; +const browserMap = { + chromium: 'Chrome Headless', + firefox: 'Firefox', + webkit: 'Safari' +} test.describe('Custom navigator.userAgent tests', () => { @@ -42,42 +47,48 @@ test.describe('User-defined user-agent tests', () => { test.describe('withClientHints() tests', () => { - test('Detect custom Client Hints data', async ({ page }) => { - await page.addInitScript(() => { - Object.defineProperty(navigator, 'userAgentData', { - value: { - brands: [], - platform: '', - mobile: false, - getHighEntropyValues: () => { - return Promise.resolve({ - brands: [ - { - brand: 'Chromium', - version: '110' - }, - { - brand: 'Not(A:Brand', - version: '110' - }, - { - brand: 'New Browser', - version: '110' - } - ], - platform: 'New OS', - formFactors: 'New Form Factor' - }); + test('Detect custom Client Hints data', async ({ page, browserName }) => { + await page.addInitScript((browserName) => { + if (browserName == 'chromium') { + Object.defineProperty(navigator, 'userAgentData', { + value: { + brands: [], + platform: '', + mobile: false, + getHighEntropyValues: () => { + return Promise.resolve({ + brands: [ + { + brand: 'Chromium', + version: '110' + }, + { + brand: 'Not(A:Brand', + version: '110' + }, + { + brand: 'New Browser', + version: '110' + } + ], + platform: 'New OS', + formFactors: 'New Form Factor' + }); + } } - } - }); - }); + }); + } + }, browserName); await page.goto(localHtml); // @ts-ignore const uap = await page.evaluate(async () => await UAParser().withClientHints()); - expect(uap).toHaveProperty('browser.name', 'New Browser'); - expect(uap).toHaveProperty('os.name', 'New OS'); - expect(uap).toHaveProperty('device.type', undefined); + if (browserName == 'chromium') { + expect(uap).toHaveProperty('browser.name', 'New Browser'); + expect(uap).toHaveProperty('os.name', 'New OS'); + expect(uap).toHaveProperty('device.type', undefined); + } else { + expect(uap).toHaveProperty('browser.name', browserMap[browserName]); + } }); }); @@ -94,11 +105,6 @@ test.describe('withFeatureCheck() tests', () => { await page.goto(localHtml); // @ts-ignore let uap = await page.evaluate(() => UAParser()); - const browserMap = { - chromium: 'Chrome Headless', - firefox: 'Firefox', - webkit: 'Safari' - } expect(uap).toHaveProperty('browser.name', browserMap[browserName]); // @ts-ignore uap = await page.evaluate(() => UAParser().withFeatureCheck()); From fc851b40c5db6fe582f2bb017aec694b00b1f9f6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Nov 2024 08:33:16 +0700 Subject: [PATCH 296/388] Improve device detection: recognize MIUI as Xiaomi --- src/extensions/ua-parser-extensions.js | 2 +- src/main/ua-parser.js | 3 ++- test/data/ua/device/xiaomi.json | 27 ++++++++++++++++++++++++++ test/data/ua/os/android.json | 9 +++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 79dd0b0a9..69ae56b95 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -161,7 +161,7 @@ const ExtraDevices = Object.freeze({ /\b(zur\d{3}) b/i // Swiss ZUR Tablet ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ - /\b((zeki)?tb.*\b) b/i // Zeki Tablets + /^((zeki)?tb.*\b) b/i // Zeki Tablets ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ /\b([yr]\d{2}) b/i, diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 1f4a5ef3c..00884b16e 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -553,7 +553,8 @@ /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models - /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i // Xiaomi Mi + /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i, // Xiaomi Mi + / ([\w ]+) miui\/v?\d/i ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ // OPPO diff --git a/test/data/ua/device/xiaomi.json b/test/data/ua/device/xiaomi.json index 688bd6182..22fe005e5 100644 --- a/test/data/ua/device/xiaomi.json +++ b/test/data/ua/device/xiaomi.json @@ -1,4 +1,31 @@ [ + { + "desc": "MIUI Xiaomi Mi MIX 3 5G", + "ua": "Dalvik/2.1.0 (Linux; U; Android 9; Mi MIX 3 5G MIUI/V10.3.2.0.PEMEUVF)", + "expect": { + "vendor": "Xiaomi", + "model": "Mi MIX 3 5G", + "type": "mobile" + } + }, + { + "desc": "MIUI POCOPHONE F1", + "ua": "Dalvik/2.1.0 (Linux; U; Android 9; POCOPHONE F1 MIUI/9.6.27)", + "expect": { + "vendor": "Xiaomi", + "model": "POCOPHONE F1", + "type": "mobile" + } + }, + { + "desc": "MIUI Xiaomi M2006C3MT", + "ua": "Dalvik/2.1.0 (Linux; U; Android 10; M2006C3MT MIUI/V12.0.7.0.QCRMIXM)", + "expect": { + "vendor": "Xiaomi", + "model": "M2006C3MT", + "type": "mobile" + } + }, { "desc": "Xiaomi 2201117TG", "ua": "Mozilla/5.0 (Linux; Android 11; 2201117TG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.98 Mobile Safari/537.36", diff --git a/test/data/ua/os/android.json b/test/data/ua/os/android.json index 440fafec2..7304e1e7f 100644 --- a/test/data/ua/os/android.json +++ b/test/data/ua/os/android.json @@ -8,6 +8,15 @@ "version" : "2.2.2" } }, + { + "desc" : "MIUI", + "ua" : "Dalvik/2.1.0 (Linux; U; Android 9; Mi MIX 3 5G MIUI/V10.3.2.0.PEMEUVF)", + "expect" : + { + "name" : "Android", + "version" : "9" + } + }, { "desc" : "KTB-Nexus 5", "ua" : "APP-My App/1.0 (Linux; Android 4.2.1; Nexus 5 Build/JOP40D)", From dbd24a579f85e02f000baa9a88953ed4e39086d2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Nov 2024 13:09:02 +0700 Subject: [PATCH 297/388] Browser naming adjustments for Client Hints: - `Google Chrome` => `Chrome` - `Microsoft Edge` => `Edge` - `Android WebView` => `Chrome WebView` - `HeadlessChrome` => `Chrome Headless` --- src/main/ua-parser.js | 8 +++++++- test/unit/ua-ch.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/unit/ua-ch.js diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 00884b16e..468e456c3 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1167,9 +1167,15 @@ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; if (brands) { for (var i in brands) { - var brandName = strip(/(Google|Microsoft) /, brands[i].brand || brands[i]), + var brandName = brands[i].brand || brands[i], brandVersion = brands[i].version; if (this.itemType == UA_BROWSER && !/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && brandName != CHROMIUM))) { + brandName = strMapper(brandName, { + 'Chrome' : 'Google Chrome', + 'Edge' : 'Microsoft Edge', + 'Chrome WebView' : 'Android WebView', + 'Chrome Headless' : 'HeadlessChrome' + }); this.set(NAME, brandName) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js new file mode 100644 index 000000000..83722dee3 --- /dev/null +++ b/test/unit/ua-ch.js @@ -0,0 +1,37 @@ +const assert = require('assert'); +const { UAParser } = require('../../src/main/ua-parser'); + +describe('Browser naming adjustments', () => { + + it('Google Chrome => Chrome', () => { + const headers = { + 'sec-ch-ua' : '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', + }; + const { browser } = UAParser(headers).withClientHints(); + assert.strictEqual(browser.name, 'Chrome'); + }); + + it('Microsoft Edge => Edge', () => { + const headers = { + 'sec-ch-ua' : '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"', + }; + const { browser } = UAParser(headers).withClientHints(); + assert.strictEqual(browser.name, 'Edge'); + }); + + it('Android WebView => Chrome WebView', () => { + const headers = { + 'sec-ch-ua' : '"Android WebView";v="123", "Not:A-Brand";v="8", "Chromium";v="123"', + }; + const { browser } = UAParser(headers).withClientHints(); + assert.strictEqual(browser.name, 'Chrome WebView'); + }); + + it('HeadlessChrome => Chrome Headless', () => { + const headers = { + 'sec-ch-ua' : '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"', + }; + const { browser } = UAParser(headers).withClientHints(); + assert.strictEqual(browser.name, 'Chrome Headless'); + }); +}); \ No newline at end of file From 48c221b50b92eecb43069fe718fbf476dbc28164 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Nov 2024 14:37:50 +0700 Subject: [PATCH 298/388] Update npm version of `@playwright/test` to fix failed test in GitHub Actions --- .github/workflows/test-ci.yml | 2 +- package-lock.json | 2441 +++++++++++++-------------------- package.json | 2 +- 3 files changed, 967 insertions(+), 1478 deletions(-) diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml index 5c02b3dc5..0c46a4866 100644 --- a/.github/workflows/test-ci.yml +++ b/.github/workflows/test-ci.yml @@ -19,5 +19,5 @@ jobs: - name: Run the test run: | npm ci - npx playwright install + npx playwright install --with-deps npm test diff --git a/package-lock.json b/package-lock.json index 3e7e54781..b602a640f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@babel/parser": "7.15.8", "@babel/traverse": "7.23.2", "@jazzer.js/core": "^1.4.0", - "@playwright/test": "~1.32.2", + "@playwright/test": "^1.49.0", "@types/node": "^22.9.1", "@types/node-fetch": "^2.6.12", "jshint": "~2.13.6", @@ -50,61 +50,62 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", - "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", - "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-compilation-targets": "^7.21.4", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.4", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.4", - "@babel/types": "^7.21.4", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -115,10 +116,13 @@ } }, "node_modules/@babel/core/node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -126,179 +130,251 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/core/node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/generator/node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", - "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.21.4", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", "dev": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports/node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-module-imports/node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", "dev": true, "dependencies": { - "@babel/types": "^7.21.4" + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" @@ -317,24 +393,27 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template/node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -364,10 +443,13 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -376,14 +458,13 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -398,188 +479,57 @@ "node": ">=8" } }, - "node_modules/@jazzer.js/core": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/core/-/core-1.4.0.tgz", - "integrity": "sha512-Wy2O6bWTOtYpfaSskYUq+iWtE6zIfp2Kang+2FvT+KT6lNMhoen/tCYZqUcMdDLgKpEqIZiDoAvMej8LgZZIxg==", + "node_modules/@jazzer.js/bug-detectors": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/bug-detectors/-/bug-detectors-1.7.0.tgz", + "integrity": "sha512-ytGXkiH0G0XWXt1sm2KKdSWwEpOIN+SIe2wT6uvIO2mrUqQmLL2LnINeahL5T8L2b0+dafYDGZeVxElgsgH5eA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "dependencies": { - "@jazzer.js/hooking": "*", - "@jazzer.js/instrumentor": "*", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.5", - "tmp": "^0.2.1", - "yargs": "^17.7.1" - }, - "bin": { - "jazzer": "dist/cli.js" + "@jazzer.js/core": "1.7.0", + "@jazzer.js/hooking": "1.7.0" }, "engines": { "node": ">= 14.0.0", "npm": ">= 7.0.0" } }, - "node_modules/@jazzer.js/core/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jazzer.js/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jazzer.js/core/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jazzer.js/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jazzer.js/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jazzer.js/core/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@jazzer.js/core/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jazzer.js/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jazzer.js/core/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jazzer.js/core/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/@jazzer.js/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/core/-/core-1.7.0.tgz", + "integrity": "sha512-HGSNb8HxPjK/z84I3CZUmXd/+TqTfjjGOgXKzHK9/fmwUltLLuUcbSTkc1RDHrgT4z2EF7Tlkeo51Vp648f8Ag==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "@jazzer.js/bug-detectors": "1.7.0", + "@jazzer.js/fuzzer": "1.7.0", + "@jazzer.js/hooking": "1.7.0", + "@jazzer.js/instrumentor": "1.7.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "tmp": "^0.2.1", + "yargs": "^17.7.2" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@jazzer.js/core/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jazzer.js/core/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "bin": { + "jazzer": "dist/cli.js" }, "engines": { - "node": ">=12" - } - }, - "node_modules/@jazzer.js/core/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" + "node": ">= 14.0.0", + "npm": ">= 7.0.0" } }, "node_modules/@jazzer.js/fuzzer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/fuzzer/-/fuzzer-1.4.0.tgz", - "integrity": "sha512-GSmoMZ1KCSOJoSoxrLQRELBzQcvmfJ6+CiFWbKiSFdYc6MGr3yIpROZu77vnGUSVr8hJnDA6vffbcM9dOk5r+g==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/fuzzer/-/fuzzer-1.7.0.tgz", + "integrity": "sha512-FDTsHzUQva3G6ETowxAlBNY+aXc4J+XzKcTRtxQdIIW9wPli2qxUjlVCkPHfKCfOcdNseAygR/AIfrXfrB0iWw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "hasInstallScript": true, "dependencies": { "bindings": "^1.5.0", "cmake-js": "^7.2.1", - "node-addon-api": "^6.0.0", + "node-addon-api": "^7.0.0", "prebuild-install": "^7.1.1" }, "engines": { @@ -588,12 +538,13 @@ } }, "node_modules/@jazzer.js/hooking": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/hooking/-/hooking-1.4.0.tgz", - "integrity": "sha512-HubGSnLv8wfEoj81O0dxqyaLk69QGUvDGdRkZ4QydKmVvph70lLuH3DC4O7xHdneoA5Cv37qRHGvl5whkAEYnA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/hooking/-/hooking-1.7.0.tgz", + "integrity": "sha512-1uGhdaRVAyNTnCuThcYFnjKKWIlVnobM+LUVoRHAFe+TVFyfnV2wCKCjoj+kjZtt4fD0lL9gjkKSafbiKNzsnA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "dependencies": { - "@babel/core": "^7.21.0" + "@babel/core": "^7.22.11" }, "engines": { "node": ">= 14.0.0", @@ -601,17 +552,18 @@ } }, "node_modules/@jazzer.js/instrumentor": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/instrumentor/-/instrumentor-1.4.0.tgz", - "integrity": "sha512-DkgalciiNIPzQaVsNM1FzzdKwnMcFDaJvB8AL+byKtdz3wEj4XoGSoMDk3tJmendP59FJHaOBxSxAjPBURv2ug==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@jazzer.js/instrumentor/-/instrumentor-1.7.0.tgz", + "integrity": "sha512-YPMzL0mH8+UKGwe0r/7vhq3DsxHOxhkeqTusYTajDycwz6/51y3JC20NhX3qs1Jyd7xpIo7ANhVN0dU4V1MwMw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "dependencies": { - "@babel/core": "^7.21.0", - "@babel/generator": "^7.21.1", - "@jazzer.js/fuzzer": "*", - "@jazzer.js/hooking": "*", + "@babel/core": "^7.22.11", + "@babel/generator": "^7.22.10", + "@jazzer.js/fuzzer": "1.7.0", + "@jazzer.js/hooking": "1.7.0", "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^5.2.1", + "istanbul-lib-instrument": "^6.0.0", "proper-lockfile": "^4.1.2", "source-map-support": "^0.5.21" }, @@ -633,59 +585,53 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -722,22 +668,18 @@ } }, "node_modules/@playwright/test": { - "version": "1.32.3", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz", - "integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0.tgz", + "integrity": "sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==", "dev": true, "dependencies": { - "@types/node": "*", - "playwright-core": "1.32.3" + "playwright": "1.49.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=14" - }, - "optionalDependencies": { - "fsevents": "2.3.2" + "node": ">=18" } }, "node_modules/@sinclair/typebox": { @@ -766,30 +708,30 @@ } }, "node_modules/@types/estree": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", - "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/minimist": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.3.tgz", - "integrity": "sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", "dev": true }, "node_modules/@types/node": { - "version": "22.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", - "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/node-fetch": { @@ -803,9 +745,9 @@ } }, "node_modules/@types/normalize-package-data": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.2.tgz", - "integrity": "sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, "node_modules/@ungap/promise-all-settled": { @@ -838,37 +780,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { @@ -906,6 +839,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "delegates": "^1.0.0", @@ -972,12 +906,12 @@ "dev": true }, "node_modules/axios": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", - "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", + "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -1009,12 +943,15 @@ ] }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bindings": { @@ -1071,12 +1008,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1089,9 +1026,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -1101,13 +1038,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -1173,9 +1114,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001481", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", - "integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==", + "version": "1.0.30001684", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", + "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", "dev": true, "funding": [ { @@ -1193,17 +1134,19 @@ ] }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chokidar": { @@ -1250,103 +1193,6 @@ } }, "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cmake-js": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.2.1.tgz", - "integrity": "sha512-AdPSz9cSIJWdKvm0aJgVu3X8i0U3mNTswJkSHzZISqmYVjZk7Td4oDFg0mCBA383wO+9pG5Ix7pEP1CZH9x2BA==", - "dev": true, - "dependencies": { - "axios": "^1.3.2", - "debug": "^4", - "fs-extra": "^10.1.0", - "lodash.isplainobject": "^4.0.6", - "memory-stream": "^1.0.0", - "node-api-headers": "^0.0.2", - "npmlog": "^6.0.2", - "rc": "^1.2.7", - "semver": "^7.3.8", - "tar": "^6.1.11", - "url-join": "^4.0.1", - "which": "^2.0.2", - "yargs": "^17.6.0" - }, - "bin": { - "cmake-js": "bin/cmake-js" - }, - "engines": { - "node": ">= 14.15.0" - } - }, - "node_modules/cmake-js/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cmake-js/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cmake-js/node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", @@ -1360,164 +1206,61 @@ "node": ">=12" } }, - "node_modules/cmake-js/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cmake-js/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/cmake-js/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/cmake-js/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cmake-js/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cmake-js/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cmake-js/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cmake-js/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cmake-js/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/cmake-js/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/cmake-js/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/cmake-js/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "node_modules/cmake-js": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.3.0.tgz", + "integrity": "sha512-dXs2zq9WxrV87bpJ+WbnGKv8WUBXDw8blNiwNHoRe/it+ptscxhQHKB1SJXa1w+kocLMeP28Tk4/eTCezg4o+w==", + "dev": true, + "dependencies": { + "axios": "^1.6.5", + "debug": "^4", + "fs-extra": "^11.2.0", + "lodash.isplainobject": "^4.0.6", + "memory-stream": "^1.0.0", + "node-api-headers": "^1.1.0", + "npmlog": "^6.0.2", + "rc": "^1.2.7", + "semver": "^7.5.4", + "tar": "^6.2.0", + "url-join": "^4.0.1", + "which": "^2.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "cmake-js": "bin/cmake-js" }, "engines": { - "node": ">=12" + "node": ">= 14.15.0" } }, - "node_modules/cmake-js/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/cmake-js/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/color-support": { @@ -1563,9 +1306,9 @@ "dev": true }, "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, "node_modules/core-util-is": { @@ -1581,12 +1324,12 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1705,9 +1448,9 @@ ] }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, "engines": { "node": ">=8" @@ -1800,15 +1543,15 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.374", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.374.tgz", - "integrity": "sha512-dNP9tQNTrjgVlSXMqGaj0BdrCS+9pcUvy5/emB6x8kh0YwCoDZ0Z4ce1+7aod+KhybHUd5o5LgKrc5al4kVmzQ==", + "version": "1.5.67", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", + "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==", "dev": true }, "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "node_modules/end-of-stream": { @@ -1836,21 +1579,24 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint-formatter-pretty": { @@ -1875,126 +1621,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-formatter-pretty/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-formatter-pretty/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint-formatter-pretty/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint-formatter-pretty/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint-formatter-pretty/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint-formatter-pretty/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/eslint-formatter-pretty/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-formatter-pretty/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-formatter-pretty/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-formatter-pretty/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-formatter-pretty/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint-rule-docs": { "version": "1.1.235", "resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.235.tgz", @@ -2033,9 +1659,9 @@ } }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2049,9 +1675,9 @@ } }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -2064,9 +1690,9 @@ "dev": true }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -2101,9 +1727,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -2121,9 +1747,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "dependencies": { "asynckit": "^0.4.0", @@ -2141,9 +1767,9 @@ "dev": true }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -2151,7 +1777,7 @@ "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.14" } }, "node_modules/fs-minipass": { @@ -2190,16 +1816,34 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gauge": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -2215,56 +1859,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/gauge/node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -2302,6 +1896,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -2395,25 +1990,13 @@ "node": ">=6" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-unicode": { @@ -2422,6 +2005,18 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -2501,9 +2096,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -2522,6 +2117,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -2568,12 +2164,15 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2589,12 +2188,12 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/is-glob": { @@ -2619,12 +2218,12 @@ } }, "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/is-standalone-pwa": { @@ -2659,9 +2258,9 @@ "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" @@ -2680,60 +2279,66 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "node_modules/istanbul-lib-instrument/node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -2758,76 +2363,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", @@ -2857,15 +2392,15 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/jshint": { @@ -2970,76 +2505,6 @@ "node": ">=10" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3050,20 +2515,32 @@ } }, "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -3165,12 +2642,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -3254,19 +2731,10 @@ "node": ">= 6" } }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, "engines": { "node": ">=8" @@ -3365,6 +2833,53 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/mocha/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, "node_modules/mocha/node_modules/debug": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", @@ -3375,30 +2890,25 @@ "ms": "2.1.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "node_modules/mocha/node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -3415,13 +2925,26 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/mocha/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/mocha/node_modules/minimatch": { @@ -3436,16 +2959,72 @@ "node": "*" } }, - "node_modules/mocha/node_modules/nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "node_modules/mocha/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "p-try": "^2.0.0" }, "engines": { - "node": "^10 || ^12 || >=13.7" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/mocha/node_modules/strip-json-comments": { @@ -3460,24 +3039,74 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/mocha/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/mocha/node_modules/yargs/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || >=13.7" + } + }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -3485,9 +3114,9 @@ "dev": true }, "node_modules/node-abi": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz", - "integrity": "sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==", + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", "dev": true, "dependencies": { "semver": "^7.3.5" @@ -3496,26 +3125,11 @@ "node": ">=10" } }, - "node_modules/node-abi/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-abi/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -3523,22 +3137,16 @@ "node": ">=10" } }, - "node_modules/node-abi/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "dev": true }, "node_modules/node-api-headers": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-0.0.2.tgz", - "integrity": "sha512-YsjmaKGPDkmhoNKIpkChtCsPVaRE0a274IdERKnuc/E8K1UJdBZ4/mvI006OijlQZHCfpRNOH3dfHQs92se8gg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.4.0.tgz", + "integrity": "sha512-u83U3WnRbBpWlhc0sQbpF3slHRLV/a6/OXByc+QzHcLxiDiJUWLuKGZp4/ntZUchnXGOCnCq++JUEtwb1/tyow==", "dev": true }, "node_modules/node-fetch": { @@ -3562,9 +3170,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/normalize-package-data": { @@ -3582,26 +3190,11 @@ "node": ">=10" } }, - "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -3609,12 +3202,6 @@ "node": ">=10" } }, - "node_modules/normalize-package-data/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3628,6 +3215,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { "are-we-there-yet": "^3.0.0", @@ -3739,9 +3327,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/picomatch": { @@ -3756,16 +3344,48 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/playwright-core": { - "version": "1.32.3", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.3.tgz", - "integrity": "sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==", + "node_modules/playwright": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0.tgz", + "integrity": "sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==", "dev": true, + "dependencies": { + "playwright-core": "1.49.0" + }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=14" + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz", + "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/plur": { @@ -3784,9 +3404,9 @@ } }, "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", "dev": true, "dependencies": { "detect-libc": "^2.0.0", @@ -3853,9 +3473,9 @@ "dev": true }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, "dependencies": { "end-of-stream": "^1.1.0", @@ -3925,9 +3545,9 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/read-pkg": { @@ -4097,9 +3717,9 @@ } }, "node_modules/regexp-tree": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", - "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, "bin": { "regexp-tree": "bin/regexp-tree" @@ -4134,9 +3754,9 @@ } }, "node_modules/resolve": { - "version": "1.22.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", - "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -4169,21 +3789,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4237,9 +3842,9 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -4350,9 +3955,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -4366,9 +3971,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.15.tgz", - "integrity": "sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, "node_modules/sprintf-js": { @@ -4384,28 +3989,29 @@ "dev": true }, "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-bom": { @@ -4442,15 +4048,15 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/supports-hyperlinks": { @@ -4466,27 +4072,6 @@ "node": ">=8" } }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -4500,14 +4085,14 @@ } }, "node_modules/tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -4580,24 +4165,12 @@ "dev": true }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, "engines": { - "node": ">=4" + "node": ">=14.14" } }, "node_modules/to-regex-range": { @@ -4660,6 +4233,18 @@ "node": "*" } }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ua-is-frozen": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ua-is-frozen/-/ua-is-frozen-0.1.2.tgz", @@ -4692,24 +4277,24 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -4726,8 +4311,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -4790,9 +4375,9 @@ } }, "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "dev": true }, "node_modules/wide-align": { @@ -4804,59 +4389,70 @@ "string-width": "^1.0.2 || 2" } }, - "node_modules/workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "dependencies": { - "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "strip-ansi": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "dependencies": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=4" + } + }, + "node_modules/workerpool": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrappy": { @@ -4866,10 +4462,13 @@ "dev": true }, "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } }, "node_modules/yallist": { "version": "3.1.1", @@ -4878,21 +4477,21 @@ "dev": true }, "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, "node_modules/yargs-parser": { @@ -4944,100 +4543,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, "engines": { - "node": ">=6" + "node": ">=12" } }, "node_modules/yocto-queue": { @@ -5051,38 +4572,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "src/client-hints-helpers": { - "name": "@ua-parser-js/client-hints-helpers", - "version": "0.0.1", - "extraneous": true, - "license": "MIT" - }, - "src/gpu-detect": { - "version": "0.0.1", - "extraneous": true, - "license": "MIT" - }, - "src/helpers": { - "name": "@ua-parser-js/helpers", - "version": "0.0.3", - "extraneous": true, - "license": "MIT" - }, - "src/ua-client-hints": { - "name": "@ua-parser-js/ua-client-hints", - "version": "0.0.1", - "extraneous": true, - "license": "MIT" - }, - "src/user-agent-helpers": { - "name": "@ua-parser-js/user-agent-helpers", - "version": "0.0.2", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@ua-parser-js/client-hints-helpers": "*" - } } } } diff --git a/package.json b/package.json index b9d958133..76c928824 100755 --- a/package.json +++ b/package.json @@ -220,7 +220,7 @@ "@babel/parser": "7.15.8", "@babel/traverse": "7.23.2", "@jazzer.js/core": "^1.4.0", - "@playwright/test": "~1.32.2", + "@playwright/test": "^1.49.0", "@types/node": "^22.9.1", "@types/node-fetch": "^2.6.12", "jshint": "~2.13.6", From aed89f0b414fe75d843f983636d389bc1f94147e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Nov 2024 18:55:27 +0700 Subject: [PATCH 299/388] Fix #660 - Infer device vendor & type from `sec-ch-ua-model` --- src/main/ua-parser.js | 15 ++-- test/unit/main.js | 4 +- test/unit/ua-ch.js | 185 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 7 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 468e456c3..7edc48da2 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1200,11 +1200,16 @@ } if (uaCH[MODEL]) { this.set(MODEL, uaCH[MODEL]); - } - // Xbox-Specific Detection - if (uaCH[MODEL] == 'Xbox') { - this.set(TYPE, CONSOLE) - .set(VENDOR, MICROSOFT); + if (!this.get(TYPE) || !this.get(VENDOR)) { + var reParse = {}; + rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap); + if (!this.get(TYPE) && !!reParse.type) { + this.set(TYPE, reParse.type); + } + if (!this.get(VENDOR) && !!reParse.vendor) { + this.set(VENDOR, reParse.vendor); + } + } } if (uaCH[FORMFACTORS]) { var ff; diff --git a/test/unit/main.js b/test/unit/main.js index 04338821c..ea337f956 100644 --- a/test/unit/main.js +++ b/test/unit/main.js @@ -390,10 +390,10 @@ describe('Map UA-CH headers', function () { assert.strictEqual(cpu.architecture, "arm64"); assert.strictEqual(uap.device.type, "mobile"); assert.strictEqual(uap.device.model, "Pixel 99"); - assert.strictEqual(uap.device.vendor, undefined); + assert.strictEqual(uap.device.vendor, "Google"); assert.strictEqual(device.type, "mobile"); assert.strictEqual(device.model, "Pixel 99"); - assert.strictEqual(device.vendor, undefined); + assert.strictEqual(device.vendor, "Google"); assert.strictEqual(uap.engine.name, 'Blink'); assert.strictEqual(uap.engine.version, '93.0.1.2'); assert.strictEqual(engine.name, 'Blink'); diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 83722dee3..84dc1fd8d 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -34,4 +34,189 @@ describe('Browser naming adjustments', () => { const { browser } = UAParser(headers).withClientHints(); assert.strictEqual(browser.name, 'Chrome Headless'); }); +}); + +describe('Identify vendor & type of device from given model name', () => { + + [ + { + model: '220733SG', + expect: { + vendor : 'Xiaomi', + type : 'mobile' + } + }, + { + model: '5087Z', + expect: { + vendor : 'TCL', + type : 'mobile' + } + }, + { + model: '9137W', + expect: { + vendor : 'TCL', + type : 'tablet' + } + }, + { + model: 'BE2015', + expect: { + vendor : 'OnePlus', + type : 'mobile' + } + }, + { + model: 'CPH2389', + expect: { + vendor : 'OPPO', + type : 'mobile' + } + }, + { + model: 'Infinix X669C', + expect: { + vendor : 'Infinix', + type : 'mobile' + } + }, + { + model: 'itel L6502', + expect: { + vendor : 'itel', + type : 'mobile' + } + }, + { + model: 'Lenovo TB-X606F', + expect: { + vendor : 'Lenovo', + type : 'tablet' + } + }, + { + model: 'LM-Q720', + expect: { + vendor : 'LG', + type : 'mobile' + } + }, + { + model: 'M2003J15SC', + expect: { + vendor : 'Xiaomi', + type : 'mobile' + } + }, + { + model: 'MAR-LX1A', + expect: { + vendor : 'Huawei', + type : 'mobile' + } + }, + { + model: 'moto g(20)', + expect: { + vendor : 'Motorola', + type : 'mobile' + } + }, + { + model: 'Nokia C210', + expect: { + vendor : 'Nokia', + type : 'mobile' + } + }, + { + model: 'Pixel 8', + expect: { + vendor : 'Google', + type : 'mobile' + } + }, + { + model: 'Redmi Note 9S', + expect: { + vendor : 'Xiaomi', + type : 'mobile' + } + }, + { + model: 'RMX3830', + expect: { + vendor : 'Realme', + type : 'mobile' + } + }, + { + model: 'SM-S536DL', + expect: { + vendor : 'Samsung', + type : 'mobile' + } + }, + { + model: 'SM-S546VL', + expect: { + vendor : 'Samsung', + type : 'mobile' + } + }, + { + model: 'SM-T875', + expect: { + vendor : 'Samsung', + type : 'tablet' + } + }, + { + model: 'STK-L21', + expect: { + vendor : 'Huawei', + type : 'mobile' + } + }, + { + model: 'T430W', + expect: { + vendor : 'TCL', + type : 'mobile' + } + }, + { + model: 'TECNO KI5k', + expect: { + vendor : 'TECNO', + type : 'mobile' + } + }, + { + model: 'vivo 1820', + expect: { + vendor : 'Vivo', + type : 'mobile' + } + }, + { + model: 'Xbox', + expect: { + vendor : 'Microsoft', + type : 'console' + } + } + ] + .forEach((test) => { + it(`Solve "${test.model}"`, () => { + const headers = { + 'sec-ch-ua-model' : test.model, + }; + const { device } = UAParser(headers).withClientHints(); + assert.strictEqual(device.model, test.model); + assert.strictEqual(device.vendor, test.expect.vendor); + assert.strictEqual(device.type, test.expect.type); + }); + }); }); \ No newline at end of file From 259cd1422bc12f530b7f92fe40d1187dddd5ee7a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Nov 2024 19:31:53 +0700 Subject: [PATCH 300/388] [test] Move all client hints-related tests into a separate file --- test/unit/main.js | 220 ------------------------------------ test/unit/ua-ch.js | 269 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 247 insertions(+), 242 deletions(-) diff --git a/test/unit/main.js b/test/unit/main.js index ea337f956..3f01d194f 100644 --- a/test/unit/main.js +++ b/test/unit/main.js @@ -353,224 +353,4 @@ describe('Read user-agent data from req.headers', function () { const { browser } = UAParser(reqHeaders); assert.strictEqual(browser.is('Midori'), true); }); -}); - -describe('Map UA-CH headers', function () { - - const headers = { - 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', - 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', - 'sec-ch-ua-arch' : '"arm"', - 'sec-ch-ua-bitness' : '"64"', - 'sec-ch-ua-mobile' : '?1', - 'sec-ch-ua-model' : '"Pixel 99"', - 'sec-ch-ua-platform' : '"Windows"', - 'sec-ch-ua-platform-version' : '"13"', - 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' - }; - - let uap = UAParser(headers).withClientHints(); - let browser = new UAParser(headers).getBrowser().withClientHints(); - let cpu = new UAParser(headers).getCPU().withClientHints(); - let device = new UAParser(headers).getDevice().withClientHints(); - let engine = new UAParser(headers).getEngine().withClientHints(); - let os = new UAParser(headers).getOS().withClientHints(); - - it('Can read from client-hints headers using `withClientHints()`', function () { - - //assert.deepEqual(uap.ua_ch, ua_ch); - assert.strictEqual(uap.ua, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"); - assert.strictEqual(uap.browser.name, "Chrome"); - assert.strictEqual(uap.browser.version, "93.0.1.2"); - assert.strictEqual(uap.browser.major, "93"); - assert.strictEqual(browser.name, "Chrome"); - assert.strictEqual(browser.version, "93.0.1.2"); - assert.strictEqual(browser.major, "93"); - assert.strictEqual(uap.cpu.architecture, "arm64"); - assert.strictEqual(cpu.architecture, "arm64"); - assert.strictEqual(uap.device.type, "mobile"); - assert.strictEqual(uap.device.model, "Pixel 99"); - assert.strictEqual(uap.device.vendor, "Google"); - assert.strictEqual(device.type, "mobile"); - assert.strictEqual(device.model, "Pixel 99"); - assert.strictEqual(device.vendor, "Google"); - assert.strictEqual(uap.engine.name, 'Blink'); - assert.strictEqual(uap.engine.version, '93.0.1.2'); - assert.strictEqual(engine.name, 'Blink'); - assert.strictEqual(engine.version, '93.0.1.2'); - assert.strictEqual(uap.os.name, "Windows"); - assert.strictEqual(uap.os.version, "11"); - assert.strictEqual(os.name, "Windows"); - assert.strictEqual(os.version, "11"); - }); - - it('Only read from user-agent header when called without `withClientHints()`', function () { - - uap = UAParser(headers); - browser = new UAParser(headers).getBrowser(); - cpu = new UAParser(headers).getCPU(); - device = new UAParser(headers).getDevice(); - engine = new UAParser(headers).getEngine(); - os = new UAParser(headers).getOS(); - - assert.strictEqual(uap.browser.name, "Chrome"); - assert.strictEqual(uap.browser.version, "110.0.0.0"); - assert.strictEqual(uap.browser.major, "110"); - assert.strictEqual(uap.cpu.architecture, "amd64"); - assert.strictEqual(uap.device.type, undefined); - assert.strictEqual(uap.device.model, undefined); - assert.strictEqual(uap.device.vendor, undefined); - assert.strictEqual(uap.engine.name, 'Blink'); - assert.strictEqual(uap.engine.version, '110.0.0.0'); - assert.strictEqual(uap.os.name, "Linux"); - assert.strictEqual(uap.os.version, "x86_64"); - }); - - it('Fallback to user-agent header when using `withClientHints()` but found no client hints-related headers', function () { - - const headers2 = { - 'sec-ch-ua-mobile' : '?1', - 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' - }; - - uap = UAParser(headers2).withClientHints(); - - assert.strictEqual(uap.browser.name, "Chrome"); - assert.strictEqual(uap.browser.version, "110.0.0.0"); - assert.strictEqual(uap.browser.major, "110"); - assert.strictEqual(uap.cpu.architecture, "amd64"); - assert.strictEqual(uap.device.type, "mobile"); - assert.strictEqual(uap.device.model, undefined); - assert.strictEqual(uap.device.vendor, undefined); - assert.strictEqual(uap.engine.name, 'Blink'); - assert.strictEqual(uap.engine.version, '110.0.0.0'); - assert.strictEqual(uap.os.name, "Linux"); - assert.strictEqual(uap.os.version, "x86_64"); - }); - - it('Can detect Apple silicon from client hints data', function () { - - // https://github.com/faisalman/ua-parser-js/issues/489#issuecomment-1479213579 - const httpHeadersFromAppleSilicon = { - 'sec-ch-ua-arch' : 'arm', - 'sec-ch-ua-platform' : 'macOS', - 'sec-ch-ua-mobile' : '?0', - 'sec-ch-ua' : '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', - 'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0' - }; - - UAParser(httpHeadersFromAppleSilicon).withClientHints().then(function (ua) { - - // Only works in Chrome - /* - if (ua.os.is("macOS") && - ua.cpu.is("arm") && - !ua.device.is("mobile") && - !ua.device.is("tablet")) { - // possibly an Apple silicon device - } - */ - - assert.strictEqual(ua.os.is("macOS"), true); - assert.strictEqual(ua.cpu.is("arm"), true); - assert.strictEqual(ua.device.is("mobile"), false); - assert.strictEqual(ua.device.is("tablet"), false); - }); - }); - - it('Can detect form-factors from client-hints', function () { - - const FFVR = { - 'sec-ch-ua-form-factors' : '"VR"' - }; - - const FFEInk = { - 'sec-ch-ua-form-factors' : '"Tablet", "EInk"' - }; - - const FFUnknown = { - 'sec-ch-ua-form-factors' : '"Unknown"' - }; - - UAParser(FFVR).withClientHints().then(function (ua) { - assert.strictEqual(ua.device.type, 'xr'); - }); - - UAParser(FFEInk).withClientHints().then(function (ua) { - assert.strictEqual(ua.device.type, 'tablet'); - }); - - - UAParser(FFUnknown).withClientHints().then(function (ua) { - assert.strictEqual(ua.device.type, undefined); - }); - }); - - it('Avoid error on headers variation', function () { - - const headers2 = { - 'sec-ch-ua' : '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"', - 'sec-ch-ua-full-version-list' : '"Google Chrome", "Chromium", "Not?A_Brand";v="24.0.0.0"', - 'sec-ch-ua-full-version' : '""', - 'sec-ch-ua-mobile' : '?0', - 'sec-ch-ua-arch' : '""', - 'sec-ch-ua-bitness' : '""', - 'sec-ch-ua-model' : '""', - 'sec-ch-ua-platform' : '"Windows"', - 'sec-ch-ua-platform-version' : '""', - 'sec-ch-ua-wow64' : '?0', - }; - - uap = UAParser(headers2).withClientHints(); - - assert.strictEqual(uap.browser.name, "Chrome"); - assert.strictEqual(uap.browser.version, undefined); - assert.strictEqual(uap.browser.major, undefined); - }); - - it('Prioritize more specific brand name regardless the order', function () { - - const headers3a = { - 'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132"' - }; - const headers3b = { - 'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Not_A Brand;v=8, Google Chrome;v=120.0.6099.132"' - }; - const headers3c = { - 'sec-ch-ua-full-version-list' : '"Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"' - }; - const headers3d = { - 'sec-ch-ua-full-version-list' : '"Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"' - }; - const headers3e = { - 'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132, Microsoft Edge;v=120.0.6099.133, Not_A Brand;v=8"' - }; - const headers3f = { - 'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131"' - }; - - uap = UAParser(headers3a).withClientHints(); - assert.strictEqual(uap.browser.name, "Chrome"); - assert.strictEqual(uap.browser.version, "120.0.6099.132"); - - uap = UAParser(headers3b).withClientHints(); - assert.strictEqual(uap.browser.name, "Chrome"); - assert.strictEqual(uap.browser.version, "120.0.6099.132"); - - uap = UAParser(headers3c).withClientHints(); - assert.strictEqual(uap.browser.name, "Chrome"); - assert.strictEqual(uap.browser.version, "120.0.6099.132"); - - uap = UAParser(headers3d).withClientHints(); - assert.strictEqual(uap.browser.name, "Edge"); - assert.strictEqual(uap.browser.version, "120.0.6099.133"); - - uap = UAParser(headers3e).withClientHints(); - assert.strictEqual(uap.browser.name, "Edge"); - assert.strictEqual(uap.browser.version, "120.0.6099.133"); - - uap = UAParser(headers3f).withClientHints(); - assert.strictEqual(uap.browser.name, "Edge"); - assert.strictEqual(uap.browser.version, "120.0.6099.133"); - }); }); \ No newline at end of file diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 84dc1fd8d..1342a3563 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -1,43 +1,268 @@ const assert = require('assert'); const { UAParser } = require('../../src/main/ua-parser'); -describe('Browser naming adjustments', () => { +describe('Map UA-CH headers', () => { + + const headers = { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : '"arm"', + 'sec-ch-ua-bitness' : '"64"', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : '"Pixel 99"', + 'sec-ch-ua-platform' : '"Windows"', + 'sec-ch-ua-platform-version' : '"13"', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; + + let uap = UAParser(headers).withClientHints(); + let browser = new UAParser(headers).getBrowser().withClientHints(); + let cpu = new UAParser(headers).getCPU().withClientHints(); + let device = new UAParser(headers).getDevice().withClientHints(); + let engine = new UAParser(headers).getEngine().withClientHints(); + let os = new UAParser(headers).getOS().withClientHints(); + + it('Can read from client-hints headers using `withClientHints()`', () => { + + assert.strictEqual(uap.ua, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "93.0.1.2"); + assert.strictEqual(uap.browser.major, "93"); + assert.strictEqual(browser.name, "Chrome"); + assert.strictEqual(browser.version, "93.0.1.2"); + assert.strictEqual(browser.major, "93"); + assert.strictEqual(uap.cpu.architecture, "arm64"); + assert.strictEqual(cpu.architecture, "arm64"); + assert.strictEqual(uap.device.type, "mobile"); + assert.strictEqual(uap.device.model, "Pixel 99"); + assert.strictEqual(uap.device.vendor, "Google"); + assert.strictEqual(device.type, "mobile"); + assert.strictEqual(device.model, "Pixel 99"); + assert.strictEqual(device.vendor, "Google"); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '93.0.1.2'); + assert.strictEqual(engine.name, 'Blink'); + assert.strictEqual(engine.version, '93.0.1.2'); + assert.strictEqual(uap.os.name, "Windows"); + assert.strictEqual(uap.os.version, "11"); + assert.strictEqual(os.name, "Windows"); + assert.strictEqual(os.version, "11"); + }); + + it('Only read from user-agent header when called without `withClientHints()`', () => { + + uap = UAParser(headers); + browser = new UAParser(headers).getBrowser(); + cpu = new UAParser(headers).getCPU(); + device = new UAParser(headers).getDevice(); + engine = new UAParser(headers).getEngine(); + os = new UAParser(headers).getOS(); + + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "110.0.0.0"); + assert.strictEqual(uap.browser.major, "110"); + assert.strictEqual(uap.cpu.architecture, "amd64"); + assert.strictEqual(uap.device.type, undefined); + assert.strictEqual(uap.device.model, undefined); + assert.strictEqual(uap.device.vendor, undefined); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '110.0.0.0'); + assert.strictEqual(uap.os.name, "Linux"); + assert.strictEqual(uap.os.version, "x86_64"); + }); - it('Google Chrome => Chrome', () => { - const headers = { + it('Fallback to user-agent header when using `withClientHints()` but found no client hints-related headers', () => { + + const headers2 = { + 'sec-ch-ua-mobile' : '?1', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; + + uap = UAParser(headers2).withClientHints(); + + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "110.0.0.0"); + assert.strictEqual(uap.browser.major, "110"); + assert.strictEqual(uap.cpu.architecture, "amd64"); + assert.strictEqual(uap.device.type, "mobile"); + assert.strictEqual(uap.device.model, undefined); + assert.strictEqual(uap.device.vendor, undefined); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '110.0.0.0'); + assert.strictEqual(uap.os.name, "Linux"); + assert.strictEqual(uap.os.version, "x86_64"); + }); + + it('Can detect Apple silicon from client hints data', () => { + + // https://github.com/faisalman/ua-parser-js/issues/489#issuecomment-1479213579 + const httpHeadersFromAppleSilicon = { + 'sec-ch-ua-arch' : 'arm', + 'sec-ch-ua-platform' : 'macOS', + 'sec-ch-ua-mobile' : '?0', 'sec-ch-ua' : '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', + 'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0' }; - const { browser } = UAParser(headers).withClientHints(); - assert.strictEqual(browser.name, 'Chrome'); + + UAParser(httpHeadersFromAppleSilicon).withClientHints().then(ua => { + + // Only works in Chrome + /* + if (ua.os.is("macOS") && + ua.cpu.is("arm") && + !ua.device.is("mobile") && + !ua.device.is("tablet")) { + // possibly an Apple silicon device + } + */ + + assert.strictEqual(ua.os.is("macOS"), true); + assert.strictEqual(ua.cpu.is("arm"), true); + assert.strictEqual(ua.device.is("mobile"), false); + assert.strictEqual(ua.device.is("tablet"), false); + }); }); - it('Microsoft Edge => Edge', () => { - const headers = { - 'sec-ch-ua' : '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"', + it('Can detect form-factors from client-hints', () => { + + const FFVR = { + 'sec-ch-ua-form-factors' : '"VR"' }; - const { browser } = UAParser(headers).withClientHints(); - assert.strictEqual(browser.name, 'Edge'); + + const FFEInk = { + 'sec-ch-ua-form-factors' : '"Tablet", "EInk"' + }; + + const FFUnknown = { + 'sec-ch-ua-form-factors' : '"Unknown"' + }; + + UAParser(FFVR).withClientHints().then(ua => { + assert.strictEqual(ua.device.type, 'xr'); + }); + + UAParser(FFEInk).withClientHints().then(ua => { + assert.strictEqual(ua.device.type, 'tablet'); + }); + + + UAParser(FFUnknown).withClientHints().then(ua => { + assert.strictEqual(ua.device.type, undefined); + }); }); - it('Android WebView => Chrome WebView', () => { - const headers = { - 'sec-ch-ua' : '"Android WebView";v="123", "Not:A-Brand";v="8", "Chromium";v="123"', + it('Avoid error on headers variation', () => { + + const headers2 = { + 'sec-ch-ua' : '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"', + 'sec-ch-ua-full-version-list' : '"Google Chrome", "Chromium", "Not?A_Brand";v="24.0.0.0"', + 'sec-ch-ua-full-version' : '""', + 'sec-ch-ua-mobile' : '?0', + 'sec-ch-ua-arch' : '""', + 'sec-ch-ua-bitness' : '""', + 'sec-ch-ua-model' : '""', + 'sec-ch-ua-platform' : '"Windows"', + 'sec-ch-ua-platform-version' : '""', + 'sec-ch-ua-wow64' : '?0', }; - const { browser } = UAParser(headers).withClientHints(); - assert.strictEqual(browser.name, 'Chrome WebView'); + + uap = UAParser(headers2).withClientHints(); + + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, undefined); + assert.strictEqual(uap.browser.major, undefined); }); - it('HeadlessChrome => Chrome Headless', () => { - const headers = { - 'sec-ch-ua' : '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"', + it('Prioritize more specific brand name regardless the order', () => { + + const headers3a = { + 'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132"' + }; + const headers3b = { + 'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Not_A Brand;v=8, Google Chrome;v=120.0.6099.132"' + }; + const headers3c = { + 'sec-ch-ua-full-version-list' : '"Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"' }; - const { browser } = UAParser(headers).withClientHints(); - assert.strictEqual(browser.name, 'Chrome Headless'); + const headers3d = { + 'sec-ch-ua-full-version-list' : '"Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"' + }; + const headers3e = { + 'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132, Microsoft Edge;v=120.0.6099.133, Not_A Brand;v=8"' + }; + const headers3f = { + 'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131"' + }; + + uap = UAParser(headers3a).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3b).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3c).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3d).withClientHints(); + assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); + + uap = UAParser(headers3e).withClientHints(); + assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); + + uap = UAParser(headers3f).withClientHints(); + assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); }); }); -describe('Identify vendor & type of device from given model name', () => { +describe('Browser naming adjustments', () => { + [ + { + ua_ch: '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', + expect: { + before: 'Google Chrome', + after: 'Chrome' + } + }, + { + ua_ch: '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"', + expect: { + before: "Microsoft Edge", + after: "Edge" + } + }, + { + ua_ch: '"Android WebView";v="123", "Not:A-Brand";v="8", "Chromium";v="123"', + expect: { + before: "Android WebView", + after: "Chrome WebView" + } + }, + { + ua_ch: '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"', + expect: { + before: "HeadlessChrome", + after: "Chrome Headless" + } + }, + ] + .forEach(test => { + it(`"${test.expect.before}" => "${test.expect.after}"`, () => { + const headers = { + 'sec-ch-ua' : test.ua_ch, + }; + const { browser } = UAParser(headers).withClientHints(); + assert.strictEqual(browser.name, test.expect.after); + }); + }); +}); +describe('Identify vendor & type of device from given model name', () => { [ { model: '220733SG', @@ -208,7 +433,7 @@ describe('Identify vendor & type of device from given model name', () => { } } ] - .forEach((test) => { + .forEach(test => { it(`Solve "${test.model}"`, () => { const headers = { 'sec-ch-ua-model' : test.model, From fbadfd7aceaf1c416fc4a9147942a61f3ca67188 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Nov 2024 22:51:29 +0700 Subject: [PATCH 301/388] Improve device detection for wearables --- src/main/ua-parser.js | 25 ++++++++++++++------- test/data/ua/device/asus.json | 9 ++++++++ test/data/ua/device/lg.json | 18 ++++++++++++++++ test/data/ua/device/motorola.json | 9 ++++++++ test/data/ua/device/oneplus.json | 9 ++++++++ test/data/ua/device/oppo.json | 36 +++++++++++++++++++++++++++++++ test/data/ua/device/samsung.json | 18 ++++++++++++++++ test/data/ua/device/sony.json | 9 ++++++++ 8 files changed, 125 insertions(+), 8 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7edc48da2..87fac2a4a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -76,6 +76,7 @@ LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', + ONEPLUS = 'OnePlus', OPPO = 'OPPO', SAMSUNG = 'Samsung', SHARP = 'Sharp', @@ -576,7 +577,7 @@ // Motorola /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, /\bmot(?:orola)?[- ](\w*)/i, - /((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i + /((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ /\b(mz60\d|xoom[2 ]{0,2}) build\//i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ @@ -585,7 +586,7 @@ /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, - /\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i, + /\blg[-e;\/ ]+((?!browser|netcast|android tv|watch)\w+)/i, /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ @@ -617,7 +618,7 @@ // OnePlus / (kb2005|in20[12]5|be20[12][59])\b/i, /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i - ], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [ + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ // Amazon /(alexa)webm/i, @@ -697,14 +698,12 @@ /(infinix) (x1101b?)/i // Infinix XPad ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan /; (hmd|imo) ([\w ]+?)(?: bui|\))/i, // HMD/IMO /(hp) ([\w ]+\w)/i, // HP iPAQ - /(asus)-?(\w+)/i, // Asus /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo - /(jolla)/i, // Jolla /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ @@ -784,13 +783,23 @@ // WEARABLES /////////////////// - /\b(sm-[lr]\d\d[05][fnuw]?s?)\b/i // Samsung Galaxy Watch + /\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i // Samsung Galaxy Watch ], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [ /((pebble))app/i, // Pebble - /(google) (pixel watch[\w ]*)( bui|\))/i // Pixel Watch + /(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ + /(ow(?:19|20)?we?[1-3]{1,3})/i // Oppo Watch + ], [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ + /(opwwe\d{3})/i // OnePlus Watch + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, WEARABLE]], [ + /(moto 360)/i // Motorola 360 + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, WEARABLE]], [ + /(smartwatch 3)/i // Sony SmartWatch + ], [MODEL, [VENDOR, SONY], [TYPE, WEARABLE]], [ + /(g watch r)/i // LG G Watch R + ], [MODEL, [VENDOR, LG], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ diff --git a/test/data/ua/device/asus.json b/test/data/ua/device/asus.json index 4e3a0ef2a..e5fee47bd 100644 --- a/test/data/ua/device/asus.json +++ b/test/data/ua/device/asus.json @@ -151,5 +151,14 @@ "model": "Z00ED", "type": "mobile" } + }, + { + "desc": "ASUS ZenWatch", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; ASUS ZenWatch Build/LWX48S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/19.77.34.5 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZenWatch", + "type": "wearable" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/lg.json b/test/data/ua/device/lg.json index f0312eba5..b47e99f00 100644 --- a/test/data/ua/device/lg.json +++ b/test/data/ua/device/lg.json @@ -160,5 +160,23 @@ "model": "LK430", "type": "tablet" } + }, + { + "desc": "LG Watch Urbane", + "ua": "Mozilla/5.0 Linux; Android 7.1.1; LG Watch Urbane Build/NWD1.180306.004 AppleWebKit/537.36 KHTML, like Gecko Chrome/19.77.34.5 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "Watch Urbane", + "type": "wearable" + } + }, + { + "desc": "LG G Watch R", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; G Watch R Build/LCA44B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Crosswalk/15.44.384.12 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "G Watch R", + "type": "wearable" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/motorola.json b/test/data/ua/device/motorola.json index af6a4c878..b9227ba35 100644 --- a/test/data/ua/device/motorola.json +++ b/test/data/ua/device/motorola.json @@ -79,5 +79,14 @@ "model": "Moto E (4)", "type": "mobile" } + }, + { + "desc": "Motorola Moto 360", + "ua": "Mozilla/5.0 (Linux; Android 4.4; Moto 360 Build/KNX01S) AppleWebKit/537.36 (KHTML, like Gecko) WIB/0.9.8 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto 360", + "type": "wearable" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/oneplus.json b/test/data/ua/device/oneplus.json index 44407d4e3..74ddcf6ca 100644 --- a/test/data/ua/device/oneplus.json +++ b/test/data/ua/device/oneplus.json @@ -115,5 +115,14 @@ "model": "OPD2203", "type": "tablet" } + }, + { + "desc": "OnePlus Watch 2", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; OPWWE231 Build/TWR7.231113.001.OPWWE231_11_A.117.240703)", + "expect": { + "vendor": "OnePlus", + "model": "OPWWE231", + "type": "wearable" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/oppo.json b/test/data/ua/device/oppo.json index 250909be5..367039e05 100644 --- a/test/data/ua/device/oppo.json +++ b/test/data/ua/device/oppo.json @@ -133,5 +133,41 @@ "model": "CPH1723", "type": "mobile" } + }, + { + "desc": "OPPO Watch 46mm", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; OW19W3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OW19W3", + "type": "wearable" + } + }, + { + "desc": "OPPO Watch 41mm", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; OW19W2 Build/OPM1.171019.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OW19W2", + "type": "wearable" + } + }, + { + "desc": "OPPO Watch 2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; OW20W1 Build/OPM1.171019.026.11_A.37.210713172937; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OW20W1", + "type": "wearable" + } + }, + { + "desc": "OPPO Watch X", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; OWWE231 Build/TWR7.231113.001.OWWE231_11_A.117.240703)", + "expect": { + "vendor": "OPPO", + "model": "OWWE231", + "type": "wearable" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/samsung.json b/test/data/ua/device/samsung.json index 0edb0f0b2..56e7b73d2 100644 --- a/test/data/ua/device/samsung.json +++ b/test/data/ua/device/samsung.json @@ -242,6 +242,15 @@ "type": "wearable" } }, + { + "desc": "Samsung Galaxy Watch7", + "ua": "Dalvik/2.1.0 (Linux; U; Android 14; SM-L300 Build/AW2E.240318.016)", + "expect": { + "vendor": "Samsung", + "model": "SM-L300", + "type": "wearable" + } + }, { "desc": "Samsung Note 10.1", "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-P605) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", @@ -367,5 +376,14 @@ "model": "SM-N920C", "type": "mobile" } + }, + { + "desc": "Samsung Galaxy Gear Live", + "ua": "Mozilla/5.0 (Linux; Android 4.4; Gear Live Build/KMV78X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/19.77.34.5 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "Gear Live", + "type": "wearable" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/sony.json b/test/data/ua/device/sony.json index a4debe725..92151af84 100644 --- a/test/data/ua/device/sony.json +++ b/test/data/ua/device/sony.json @@ -205,5 +205,14 @@ "model": "PlayStation Vita", "type": "console" } + }, + { + "desc": "Sony SmartWatch 3", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; SmartWatch 3 Build/LWX49K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/19.77.34.5 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SmartWatch 3", + "type": "wearable" + } } ] \ No newline at end of file From d4986af7c092ae0058a002209c70c1822cb5fecd Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 2 Dec 2024 10:42:09 +0700 Subject: [PATCH 302/388] Improve device detection for Xiaomi TVs --- src/main/ua-parser.js | 2 +- test/data/ua/device/xiaomi.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 87fac2a4a..0eefc3040 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -755,7 +755,7 @@ ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ /(bravia[\w ]+)( bui|\))/i // Sony ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ - /(mitv-\w{5}) bui/i // Xiaomi + /(mi(tv|box)-?\w+) bui/i // Xiaomi ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ /Hbbtv.*(technisat) (.*);/i // TechniSAT ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ diff --git a/test/data/ua/device/xiaomi.json b/test/data/ua/device/xiaomi.json index 22fe005e5..86593efc0 100644 --- a/test/data/ua/device/xiaomi.json +++ b/test/data/ua/device/xiaomi.json @@ -547,5 +547,23 @@ "model": "MiTV-MOOQ0", "type": "smarttv" } + }, + { + "desc": "Xiaomi Mi TV", + "ua": "Mozilla/5.0 (Linux; Android 9; MiTV4I Build/PI; en-in) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36 Puffin/7.8.3.40913AP", + "expect": { + "vendor": "Xiaomi", + "model": "MiTV4I", + "type": "smarttv" + } + }, + { + "desc": "Xiaomi Mi Box", + "ua": "Mozilla/5.0 (Linux; Android 9; MIBOX3 Build/PI; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/119.0.6045.193 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MIBOX3", + "type": "smarttv" + } } ] \ No newline at end of file From d024fe20067fa09d8292d920ab0fd795be3b422a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 3 Dec 2024 21:32:13 +0700 Subject: [PATCH 303/388] [extensions] Fix #770 - Add new fetcher bot: `Bluesky` --- src/extensions/ua-parser-extensions.js | 26 ++++++++++---------------- test/data/ua/extension/fetcher.json | 10 ++++++++++ test/unit/extensions.js | 9 +++++++++ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 69ae56b95..09b793c61 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -218,31 +218,25 @@ const Fetchers = Object.freeze({ // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / Telegrambot / Twitterbot / UptimeRobot - /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot)\/([\w\.]+)/i, - - // Google Site Verifier - /(google-site-verification)\/([\w\.]+)/i, + // Google Site Verifier / Meta / Yahoo! Japan + // Yandex Bots - https://yandex.com/bots + /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, - // Meta - /(meta-externalfetcher)\/([\w\.]+)/i, + // Bluesky + /(bluesky) cardyb\/([\w\.]+)/i, // Slackbot - https://api.slack.com/robots /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, // WhatsApp - /(whatsapp)\/([\w\.]+)[\/ ][ianw]/i, - - // Yahoo! Japan - /(y!?j-dlc)\/([\w\.]+)/i, - - // Yandex Bots - https://yandex.com/bots - /(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, - /(yandex(?:sitelinks|userproxy))/i + /(whatsapp)\/([\w\.]+)[\/ ][ianw]/i ], [NAME, VERSION, [TYPE, FETCHER]], - // Google Bots / Cohere / Snapchat / Vercelbot - [/(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], + [ + // Google Bots / Cohere / Snapchat / Vercelbot / Yandex Bots + /(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat|yandex(?:sitelinks|userproxy))/i + ], [NAME, [TYPE, FETCHER]], ] }); diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index dfd76f16e..a04e4f677 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -19,6 +19,16 @@ "type" : "fetcher" } }, + { + "desc" : "Bluesky", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Bluesky Cardyb/1.1; +mailto:support@bsky.app) Chrome/100.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Bluesky", + "version" : "1.1", + "type" : "fetcher" + } + }, { "desc" : "ChatGPT-User", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot", diff --git a/test/unit/extensions.js b/test/unit/extensions.js index 6fd755bb1..0f04732ee 100644 --- a/test/unit/extensions.js +++ b/test/unit/extensions.js @@ -48,6 +48,15 @@ describe('Extensions', () => { assert.deepEqual(libraryParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "library"}); assert.deepEqual(libraryParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "library"}); assert.deepEqual(libraryParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "library"}); + + // Bluesky + const bluesky = 'Mozilla/5.0 (compatible; Bluesky Cardyb/1.1; +mailto:support@bsky.app)'; + assert.deepEqual(new UAParser(bluesky, Bots).getBrowser(), { + name: 'Bluesky', + version: '1.1', + major: '1', + type: 'fetcher' + }); }); describe('Merge', () => { From 592bfdd33c2c32a8b0a4786785863f2184287fe2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 8 Dec 2024 21:52:05 +0700 Subject: [PATCH 304/388] Add new browser: `Ladybird` https://ladybird.org/ --- src/enums/ua-parser-enums.js | 1 + src/helpers/ua-parser-helpers.d.ts | 2 +- src/main/ua-parser.js | 53 +++++++++++++++++++-------- test/data/ua/browser/browser-all.json | 10 +++++ test/data/ua/engine/engine-all.json | 9 +++++ 5 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 42a1462c6..3cd514815 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -76,6 +76,7 @@ const Browser = Object.freeze({ KLARNA: 'Klarna', KINDLE: 'Kindle', LENOVO: 'Smart Lenovo Browser', + LADYBIRD: 'Ladybird', LIBREWOLF: 'LibreWolf', LIEBAO: 'LBBROWSER', LINE: 'Line', diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 30a2c810b..86bfad170 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman -import { IResult } from "../main/ua-parser"; +import type { IResult } from "../main/ua-parser"; declare function getDeviceVendor(model: string): string | undefined; declare function isAppleSilicon(resultOrUA: IResult | string): boolean; diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0eefc3040..f7f2a6465 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -19,20 +19,35 @@ // Constants ///////////// - var LIBVERSION = '2.0.0', + var LIBVERSION = '2.0.0', // UAParser.version + UA_MAX_LENGTH = 500, // UA string over this length will be trimmed + USER_AGENT = 'user-agent', EMPTY = '', UNKNOWN = '?', + + // typeof FUNC_TYPE = 'function', UNDEF_TYPE = 'undefined', OBJ_TYPE = 'object', STR_TYPE = 'string', - MAJOR = 'major', - MODEL = 'model', + + // properties + UA_BROWSER = 'browser', + UA_CPU = 'cpu', + UA_DEVICE = 'device', + UA_ENGINE = 'engine', + UA_OS = 'os', + UA_RESULT = 'result', + NAME = 'name', TYPE = 'type', VENDOR = 'vendor', VERSION = 'version', ARCHITECTURE= 'architecture', + MAJOR = 'major', + MODEL = 'model', + + // device types CONSOLE = 'console', MOBILE = 'mobile', TABLET = 'tablet', @@ -40,9 +55,11 @@ WEARABLE = 'wearable', XR = 'xr', EMBEDDED = 'embedded', + + // browser types INAPP = 'inapp', - USER_AGENT = 'user-agent', - UA_MAX_LENGTH = 500, + + // client hints BRANDS = 'brands', FORMFACTORS = 'formFactors', FULLVERLIST = 'fullVersionList', @@ -59,12 +76,8 @@ CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], - UA_BROWSER = 'browser', - UA_CPU = 'cpu', - UA_DEVICE = 'device', - UA_ENGINE = 'engine', - UA_OS = 'os', - UA_RESULT = 'result', + + // device vendors AMAZON = 'Amazon', APPLE = 'Apple', ASUS = 'ASUS', @@ -83,8 +96,8 @@ SONY = 'Sony', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', - PREFIX_MOBILE = 'Mobile ', - SUFFIX_BROWSER = ' Browser', + + // browsers CHROME = 'Chrome', CHROMIUM = 'Chromium', CHROMECAST = 'Chromecast', @@ -93,6 +106,11 @@ OPERA = 'Opera', FACEBOOK = 'Facebook', SOGOU = 'Sogou', + + PREFIX_MOBILE = 'Mobile ', + SUFFIX_BROWSER = ' Browser', + + // os WINDOWS = 'Windows'; var isWindow = typeof window !== UNDEF_TYPE, @@ -464,8 +482,8 @@ /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla // Other - /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, - // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser + /(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser/Ladybird /\b(links) \(([\w\.]+)/i // Links ], [NAME, [VERSION, /_/g, '.']], [ @@ -855,8 +873,11 @@ /ekioh(flow)\/([\w\.]+)/i, // Flow /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab - /\b(libweb)/i + + /\b(libweb)/i // LibWeb ], [NAME, VERSION], [ + /ladybird\//i + ], [[NAME, 'LibWeb']], [ /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko ], [VERSION, NAME] diff --git a/test/data/ua/browser/browser-all.json b/test/data/ua/browser/browser-all.json index 869859786..45a8e0789 100644 --- a/test/data/ua/browser/browser-all.json +++ b/test/data/ua/browser/browser-all.json @@ -1076,6 +1076,16 @@ "major" : "91" } }, + { + "desc" : "Ladybird", + "ua" : "Mozilla/5.0 (Linux; x86_64) Ladybird/1.0", + "expect" : + { + "name" : "Ladybird", + "version" : "1.0", + "major" : "1" + } + }, { "desc" : "LibreWolf", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0 LibreWolf/97.0.1", diff --git a/test/data/ua/engine/engine-all.json b/test/data/ua/engine/engine-all.json index 7523fc36e..c68603112 100644 --- a/test/data/ua/engine/engine-all.json +++ b/test/data/ua/engine/engine-all.json @@ -62,6 +62,15 @@ "version" : "4.5.4" } }, + { + "desc" : "LibWeb", + "ua" : "Mozilla/5.0 (Linux; x86_64) Ladybird/1.0", + "expect" : + { + "name" : "LibWeb", + "version" : "undefined" + } + }, { "desc" : "LibWeb", "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb", From bb1daf4e132a3ffd73fbeae47aade5203bfcb3b3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 9 Dec 2024 09:49:58 +0700 Subject: [PATCH 305/388] Fix #771 - Provide alternative contribution channel for Alipay/WeChat users to support the open-source editions of UAParser.js --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd7772852..0ea5465c7 100644 --- a/README.md +++ b/README.md @@ -239,4 +239,13 @@ Made with [contributors-img](https://contrib.rocks). -Support the open-source editions of UAParser.js on [OpenCollective](https://opencollective.com/ua-parser-js) or [GitHub Sponsors](https://github.com/sponsors/faisalman). +You can support the open-source editions of UAParser.js through one of the following options: + +[![OpenCollective](https://img.shields.io/badge/OpenCollective-dddddd?style=for-the-badge&logo=opencollective&color=dddddd +)](https://opencollective.com/ua-parser-js) +[![GitHub Sponsors](https://img.shields.io/badge/GitHub_Sponsors-333333?style=for-the-badge&logo=githubsponsors&color=333333 +)](https://github.com/sponsors/faisalman) +[![PayPal](https://img.shields.io/badge/Paypal-003087?style=for-the-badge&logo=paypal&color=003087 +)](https://paypal.me/faisalman) +[![WeChat/Alipay](https://img.shields.io/badge/Other_Payment_Methods-Alipay_/_WeChat_Pay-09b83e?style=for-the-badge&logo=mastercard&color=09b83e +)](https://store.faisalman.com/buy/3d71f2f3-cf4d-473c-892a-9d4497c890be) \ No newline at end of file From f854f26cd6721029f3645b7b832c01b9de71405e Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 10 Dec 2024 03:23:01 -0800 Subject: [PATCH 306/388] feat: add TypeScript definitions for UAParser.js enums (#772) Introduce TypeScript type definitions for UAParser.js version 2.0.0. This enhances type safety and autocompletion for enums related to browsers, CPUs, devices, vendors, engines, and operating systems in the project. These changes are made to improve developer experience and ensure consistent usage of the enums throughout the codebase. --- src/enums/ua-parser-enums.d.ts | 363 +++++++++++++++++++++++++++++++++ src/enums/ua-parser-enums.mjs | 3 + 2 files changed, 366 insertions(+) create mode 100644 src/enums/ua-parser-enums.d.ts diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts new file mode 100644 index 000000000..efc73207f --- /dev/null +++ b/src/enums/ua-parser-enums.d.ts @@ -0,0 +1,363 @@ +// Type definitions for Enums submodule of UAParser.js v2.0.0 +// Project: https://github.com/faisalman/ua-parser-js +// Definitions by: Faisal Salman + +export const Browser: Readonly<{ + '115': "115"; + '2345': "2345"; + '360': "360"; + ALIPAY: "Alipay"; + AMAYA: "Amaya"; + ANDROID: "Android Browser"; + ARORA: "Arora"; + AVANT: "Avant"; + AVAST: "Avast Secure Browser"; + AVG: "AVG Secure Browser"; + BAIDU: "Baidu Browser"; + BASILISK: "Basilisk"; + BLAZER: "Blazer"; + BOLT: "Bolt"; + BOWSER: "Bowser"; + BRAVE: "Brave"; + CAMINO: "Camino"; + CHIMERA: "Chimera"; + CHROME: "Chrome"; + CHROME_HEADLESS: "Chrome Headless"; + CHROME_MOBILE: "Mobile Chrome"; + CHROME_WEBVIEW: "Chrome WebView"; + CHROMIUM: "Chromium"; + COBALT: "Cobalt"; + COC_COC: "Coc Coc"; + CONKEROR: "Conkeror"; + DILLO: "Dillo"; + DOLPHIN: "Dolphin"; + DORIS: "Doris"; + DRAGON: "Dragon"; + DUCKDUCKGO: "DuckDuckGo"; + EDGE: "Edge"; + EPIPHANY: "Epiphany"; + FACEBOOK: "Facebook"; + FALKON: "Falkon"; + FIREBIRD: "Firebird"; + FIREFOX: "Firefox"; + FIREFOX_FOCUS: "Firefox Focus"; + FIREFOX_MOBILE: "Mobile Firefox"; + FIREFOX_REALITY: "Firefox Reality"; + FENNEC: "Fennec"; + FLOCK: "Flock"; + FLOW: "Flow"; + GO: "GoBrowser"; + GOOGLE_SEARCH: "GSA"; + HELIO: "Helio"; + HEYTAP: "HeyTap"; + HONOR: "Honor"; + HUAWEI: "Huawei Browser"; + ICAB: "iCab"; + ICE: "ICE Browser"; + ICEAPE: "IceApe"; + ICECAT: "IceCat"; + ICEDRAGON: "IceDragon"; + ICEWEASEL: "IceWeasel"; + IE: "IE"; + INSTAGRAM: "Instagram"; + IRIDIUM: "Iridium"; + IRON: "Iron"; + JASMINE: "Jasmine"; + KONQUEROR: "Konqueror"; + KAKAO: "KakaoTalk"; + KHTML: "KHTML"; + K_MELEON: "K-Meleon"; + KLAR: "Klar"; + KLARNA: "Klarna"; + KINDLE: "Kindle"; + LENOVO: "Smart Lenovo Browser"; + LADYBIRD: "Ladybird"; + LIBREWOLF: "LibreWolf"; + LIEBAO: "LBBROWSER"; + LINE: "Line"; + LINKEDIN: "LinkedIn"; + LINKS: "Links"; + LUNASCAPE: "Lunascape"; + LYNX: "Lynx"; + MAEMO: "Maemo Browser"; + MAXTHON: "Maxthon"; + MIDORI: "Midori"; + MINIMO: "Minimo"; + MIUI: "MIUI Browser"; + MOZILLA: "Mozilla"; + MOSAIC: "Mosaic"; + NAVER: "Naver"; + NETFRONT: "NetFront"; + NETSCAPE: "Netscape"; + NETSURF: "Netsurf"; + NOKIA: "Nokia Browser"; + OBIGO: "Obigo"; + OCULUS: "Oculus Browser"; + OMNIWEB: "OmniWeb"; + OPERA: "Opera"; + OPERA_COAST: "Opera Coast"; + OPERA_MINI: "Opera Mini"; + OPERA_MOBI: "Opera Mobi"; + OPERA_TABLET: "Opera Tablet"; + OPERA_TOUCH: "Opera Touch"; + OVI: "OviBrowser"; + PALEMOON: "PaleMoon"; + PHANTOMJS: "PhantomJS"; + PHOENIX: "Phoenix"; + PICOBROWSER: "Pico Browser"; + POLARIS: "Polaris"; + PUFFIN: "Puffin"; + QQ: "QQBrowser"; + QQ_LITE: "QQBrowserLite"; + QUARK: "Quark"; + QUPZILLA: "QupZilla"; + REKONQ: "rekonq"; + ROCKMELT: "Rockmelt"; + SAFARI: "Safari"; + SAFARI_MOBILE: "Mobile Safari"; + SAILFISH: "Sailfish Browser"; + SAMSUNG: "Samsung Internet"; + SEAMONKEY: "SeaMonkey"; + SILK: "Silk"; + SKYFIRE: "Skyfire"; + SLEIPNIR: "Sleipnir"; + SLIMBOAT: "SlimBoat"; + SLIMBROWSER: "SlimBrowser"; + SLIMJET: "Slimjet"; + SNAPCHAT: "Snapchat"; + SOGOU_EXPLORER: "Sogou Explorer"; + SOGOU_MOBILE: "Sogou Mobile"; + SWIFTFOX: "Swiftfox"; + TESLA: "Tesla"; + TIKTOK: "TikTok"; + TIZEN: "Tizen Browser"; + TWITTER: "Twitter"; + UC: "UCBrowser"; + UP: "UP.Browser"; + VIVALDI: "Vivaldi"; + VIVO: "Vivo Browser"; + W3M: "w3m"; + WATERFOX: "Waterfox"; + WEBKIT: "WebKit"; + WECHAT: "WeChat"; + WEIBO: "Weibo"; + WHALE: "Whale"; + WOLVIC: "Wolvic"; + YANDEX: "Yandex"; +}>; +export const BrowserType: Readonly<{ + CRAWLER: "crawler"; + CLI: "cli"; + EMAIL: "email"; + FETCHER: "fetcher"; + INAPP: "inapp"; + MEDIAPLAYER: "mediaplayer"; + LIBRARY: "library"; +}>; +export const CPU: Readonly<{ + '68K': "68k"; + ARM: "arm"; + ARM_64: "arm64"; + ARM_HF: "armhf"; + AVR: "avr"; + AVR_32: "avr32"; + IA64: "ia64"; + IRIX: "irix"; + IRIX_64: "irix64"; + MIPS: "mips"; + MIPS_64: "mips64"; + PA_RISC: "pa-risc"; + PPC: "ppc"; + SPARC: "sparc"; + SPARC_64: "sparc64"; + X86: "ia32"; + X86_64: "amd64"; +}>; +export const Device: Readonly<{ + CONSOLE: "console"; + DESKTOP: "desktop"; + EMBEDDED: "embedded"; + MOBILE: "mobile"; + SMARTTV: "smarttv"; + TABLET: "tablet"; + WEARABLE: "wearable"; + XR: "xr"; +}>; +export const Vendor: Readonly<{ + ACER: "Acer"; + ADVAN: "Advan"; + ALCATEL: "Alcatel"; + APPLE: "Apple"; + AMAZON: "Amazon"; + ARCHOS: "Archos"; + ASUS: "ASUS"; + ATT: "AT&T"; + BENQ: "BenQ"; + BLACKBERRY: "BlackBerry"; + CAT: "Cat"; + DELL: "Dell"; + ENERGIZER: "Energizer"; + ESSENTIAL: "Essential"; + FACEBOOK: "Facebook"; + FAIRPHONE: "Fairphone"; + GEEKSPHONE: "GeeksPhone"; + GENERIC: "Generic"; + GOOGLE: "Google"; + HMD: "HMD"; + HP: "HP"; + HTC: "HTC"; + HUAWEI: "Huawei"; + IMO: "IMO"; + INFINIX: "Infinix"; + ITEL: "itel"; + JOLLA: "Jolla"; + KOBO: "Kobo"; + LENOVO: "Lenovo"; + LG: "LG"; + MEIZU: "Meizu"; + MICROMAX: "Micromax"; + MICROSOFT: "Microsoft"; + MOTOROLA: "Motorola"; + NEXIAN: "Nexian"; + NINTENDO: "Nintendo"; + NOKIA: "Nokia"; + NOTHING: "Nothing"; + NVIDIA: "Nvidia"; + ONEPLUS: "OnePlus"; + OPPO: "OPPO"; + OUYA: "Ouya"; + PALM: "Palm"; + PANASONIC: "Panasonic"; + PEBBLE: "Pebble"; + PICO: "Pico"; + POLYTRON: "Polytron"; + REALME: "Realme"; + RIM: "RIM"; + ROKU: "Roku"; + SAMSUNG: "Samsung"; + SHARP: "Sharp"; + SIEMENS: "Siemens"; + SMARTFREN: "Smartfren"; + SONY: "Sony"; + SPRINT: "Sprint"; + TCL: "TCL"; + TECHNISAT: "TechniSAT"; + TECNO: "Tecno"; + TESLA: "Tesla"; + ULEFONE: "Ulefone"; + VIVO: "Vivo"; + VODAFONE: "Vodafone"; + XBOX: "Xbox"; + XIAOMI: "Xiaomi"; + ZEBRA: "Zebra"; + ZTE: "ZTE"; +}>; +export const Engine: Readonly<{ + AMAYA: "Amaya"; + ARKWEB: "ArkWeb"; + BLINK: "Blink"; + EDGEHTML: "EdgeHTML"; + FLOW: "Flow"; + GECKO: "Gecko"; + GOANNA: "Goanna"; + ICAB: "iCab"; + KHTML: "KHTML"; + LIBWEB: "LibWeb"; + LINKS: "Links"; + LYNX: "Lynx"; + NETFRONT: "NetFront"; + NETSURF: "NetSurf"; + PRESTO: "Presto"; + SERVO: "Servo"; + TASMAN: "Tasman"; + TRIDENT: "Trident"; + W3M: "w3m"; + WEBKIT: "WebKit"; +}>; +export const OS: Readonly<{ + AIX: "AIX"; + AMIGA_OS: "Amiga OS"; + ANDROID: "Android"; + ANDROID_X86: "Android-x86"; + ARCH: "Arch"; + BADA: "Bada"; + BEOS: "BeOS"; + BLACKBERRY: "BlackBerry"; + CENTOS: "CentOS"; + CHROME_OS: "Chrome OS"; + CHROMECAST: "Chromecast"; + CHROMECAST_ANDROID: "Chromecast Android"; + CHROMECAST_FUCHSIA: "Chromecast Fuchsia"; + CHROMECAST_LINUX: "Chromecast Linux"; + CHROMECAST_SMARTSPEAKER: "Chromecast SmartSpeaker"; + CONTIKI: "Contiki"; + DEBIAN: "Debian"; + DEEPIN: "Deepin"; + DRAGONFLY: "DragonFly"; + ELEMENTARY_OS: "elementary OS"; + FEDORA: "Fedora"; + FIREFOX_OS: "Firefox OS"; + FREEBSD: "FreeBSD"; + FUCHSIA: "Fuchsia"; + GENTOO: "Gentoo"; + GHOSTBSD: "GhostBSD"; + GNU: "GNU"; + HAIKU: "Haiku"; + HARMONYOS: "HarmonyOS"; + HP_UX: "HP-UX"; + HURD: "Hurd"; + IOS: "iOS"; + JOLI: "Joli"; + KAIOS: "KaiOS"; + KUBUNTU: "Kubuntu"; + LINPUS: "Linpus"; + LINSPIRE: "Linspire"; + LINUX: "Linux"; + MACOS: "macOS"; + MAEMO: "Maemo"; + MAGEIA: "Mageia"; + MANDRIVA: "Mandriva"; + MANJARO: "Manjaro"; + MEEGO: "MeeGo"; + MINIX: "Minix"; + MINT: "Mint"; + MORPH_OS: "Morph OS"; + NETBSD: "NetBSD"; + NETRANGE: "NetRange"; + NETTV: "NetTV"; + NINTENDO: "Nintendo"; + OPENHARMONY: "OpenHarmony"; + OPENBSD: "OpenBSD"; + OPENVMS: "OpenVMS"; + OS2: "OS/2"; + PALM: "Palm"; + PC_BSD: "PC-BSD"; + PCLINUXOS: "PCLinuxOS"; + PICO: "Pico"; + PLAN9: "Plan9"; + PLAYSTATION: "PlayStation"; + QNX: "QNX"; + RASPBIAN: "Raspbian"; + REDHAT: "RedHat"; + RIM_TABLET_OS: "RIM Tablet OS"; + RISC_OS: "RISC OS"; + SABAYON: "Sabayon"; + SAILFISH: "Sailfish"; + SERENITYOS: "SerenityOS"; + SERIES40: "Series40"; + SLACKWARE: "Slackware"; + SOLARIS: "Solaris"; + SUSE: "SUSE"; + SYMBIAN: "Symbian"; + TIZEN: "Tizen"; + UBUNTU: "Ubuntu"; + UNIX: "Unix"; + VECTORLINUX: "VectorLinux"; + WATCHOS: "watchOS"; + WEBOS: "WebOS"; + WINDOWS: "Windows"; + WINDOWS_MOBILE: "Windows Mobile"; + WINDOWS_PHONE: "Windows Phone"; + XBOX: "Xbox"; + ZENWALK: "Zenwalk"; +}>; diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index c9a2f0fb8..276b8a268 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -80,6 +80,7 @@ const Browser = Object.freeze({ KLARNA: 'Klarna', KINDLE: 'Kindle', LENOVO: 'Smart Lenovo Browser', + LADYBIRD: 'Ladybird', LIBREWOLF: 'LibreWolf', LIEBAO: 'LBBROWSER', LINE: 'Line', @@ -217,6 +218,7 @@ const Vendor = Object.freeze({ GEEKSPHONE: 'GeeksPhone', GENERIC: 'Generic', GOOGLE: 'Google', + HMD: 'HMD', HP: 'HP', HTC: 'HTC', HUAWEI: 'Huawei', @@ -326,6 +328,7 @@ const OS = Object.freeze({ IOS: 'iOS', JOLI: 'Joli', KAIOS: 'KaiOS', + KUBUNTU: 'Kubuntu', LINPUS: 'Linpus', LINSPIRE: 'Linspire', LINUX: 'Linux', From 84b41f5be77334d46f76c55ec505acc5383c48c7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Dec 2024 06:29:35 +0700 Subject: [PATCH 307/388] Add new OS: `Windows IoT` --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 8 +++++--- test/data/ua/device/_others.json | 9 +++++++++ test/data/ua/os/windows.json | 9 +++++++++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index efc73207f..5c406c393 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -356,6 +356,7 @@ export const OS: Readonly<{ WATCHOS: "watchOS"; WEBOS: "WebOS"; WINDOWS: "Windows"; + WINDOWS_IOT: "Windows IoT"; WINDOWS_MOBILE: "Windows Mobile"; WINDOWS_PHONE: "Windows Phone"; XBOX: "Xbox"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 3cd514815..08a7afc7a 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -371,6 +371,7 @@ const OS = Object.freeze({ WATCHOS: 'watchOS', WEBOS: 'WebOS', WINDOWS: 'Windows', + WINDOWS_IOT: 'Windows IoT', WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', XBOX: 'Xbox', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index f7f2a6465..7dbe7dc28 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -840,6 +840,8 @@ ], [VENDOR, [TYPE, EMBEDDED]], [ /(aeobc)\b/i // Echo Dot ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ + /windows iot/i + ], [[TYPE, EMBEDDED]], [ //////////////////// // MIXED (GENERIC) @@ -888,10 +890,10 @@ // Windows /microsoft (windows) (vista|xp)/i // Windows (iTunes) ], [NAME, VERSION], [ - /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i // Windows Phone + /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ - /windows nt 6\.2; (arm)/i, // Windows RT - /windows[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i, + /windows nt 6\.2; (arm)/i, // Windows RT + /windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i, /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index 7ee9ec649..b830065a6 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -246,5 +246,14 @@ "model": "undefined", "type": "mobile" } + }, + { + "desc" : "Windows IoT", + "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "embedded" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json index 39e954155..7e1228226 100644 --- a/test/data/ua/os/windows.json +++ b/test/data/ua/os/windows.json @@ -116,6 +116,15 @@ "version" : "CE" } }, + { + "desc" : "Windows IoT", + "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", + "expect" : + { + "name" : "Windows IoT", + "version" : "10.0" + } + }, { "desc" : "Windows NT on x86 or aarch64 CPU using Firefox", "ua" : "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0", From 5d603dacd7fc065ee5e47471e180a164961c81fa Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Dec 2024 07:29:56 +0700 Subject: [PATCH 308/388] Add new device: Apple HomePod --- src/main/ua-parser.js | 4 +++- test/data/ua/device/apple.json | 9 +++++++++ test/data/ua/os/ios.json | 9 +++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7dbe7dc28..0fa08b985 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -840,6 +840,8 @@ ], [VENDOR, [TYPE, EMBEDDED]], [ /(aeobc)\b/i // Echo Dot ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ + /(homepod).+mac os/i // Apple HomePod + ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [ /windows iot/i ], [[TYPE, EMBEDDED]], [ @@ -898,7 +900,7 @@ ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ // iOS/macOS - /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS + /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i, /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ diff --git a/test/data/ua/device/apple.json b/test/data/ua/device/apple.json index 4de572889..ea68e8acc 100644 --- a/test/data/ua/device/apple.json +++ b/test/data/ua/device/apple.json @@ -116,6 +116,15 @@ "type": "mobile" } }, + { + "desc": "Apple HomePod", + "ua": "AppleCoreMedia/1.0.0.15D61 (HomePod; U; CPU OS 11_2_5 like Mac OS X; en_us)", + "expect": { + "vendor": "Apple", + "model": "HomePod", + "type": "embedded" + } + }, { "desc": "Issue #519", "ua": "ios/iPhone/14.2/SOME_CUSTOM_APP_VERSION", diff --git a/test/data/ua/os/ios.json b/test/data/ua/os/ios.json index 66cff1edf..86ba8cb47 100644 --- a/test/data/ua/os/ios.json +++ b/test/data/ua/os/ios.json @@ -52,5 +52,14 @@ "name" : "iOS", "version" : "7.0.2" } + }, + { + "desc": "Apple HomePod", + "ua": "AppleCoreMedia/1.0.0.15D61 (HomePod; U; CPU OS 11_2_5 like Mac OS X; en_us)", + "expect" : + { + "name" : "iOS", + "version" : "11.2.5" + } } ] \ No newline at end of file From 594b6b1399c307412ab3eeeb23dca4ac31464205 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Dec 2024 14:43:19 +0700 Subject: [PATCH 309/388] Add new OS: `Ubuntu Touch` --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 5 ++++- test/data/ua/os/ubuntu-touch.json | 11 +++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/data/ua/os/ubuntu-touch.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 5c406c393..f8cca69b4 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -351,6 +351,7 @@ export const OS: Readonly<{ SYMBIAN: "Symbian"; TIZEN: "Tizen"; UBUNTU: "Ubuntu"; + UBUNTU_TOUCH: "Ubuntu Touch", UNIX: "Unix"; VECTORLINUX: "VectorLinux"; WATCHOS: "watchOS"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 08a7afc7a..67a5447b6 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -366,6 +366,7 @@ const OS = Object.freeze({ SYMBIAN: 'Symbian', TIZEN: 'Tizen', UBUNTU: 'Ubuntu', + UBUNTU_TOUCH: 'Ubuntu Touch', UNIX: 'Unix', VECTORLINUX: 'VectorLinux', WATCHOS: 'watchOS', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0fa08b985..620c4b038 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -922,7 +922,10 @@ // Mobile OSes /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS - ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS/OpenHarmony + ], [VERSION, NAME], [ + /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch + ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ + // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS/OpenHarmony /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish|openharmony)[-\/ ]?([\w\.]*)/i, /(blackberry)\w*\/([\w\.]*)/i, // Blackberry /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS diff --git a/test/data/ua/os/ubuntu-touch.json b/test/data/ua/os/ubuntu-touch.json new file mode 100644 index 000000000..e0aea5ff2 --- /dev/null +++ b/test/data/ua/os/ubuntu-touch.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Ubuntu Touch", + "ua" : "Mozilla/5.0 (Linux; Ubuntu 16.04 like Android 4.4) AppleWebKit/537.36 Chromium/65.0.3325.151 Mobile Safari/537.36", + "expect" : + { + "name" : "Ubuntu Touch", + "version" : "16.04" + } + } +] \ No newline at end of file From 46019533264629ac7c945862f49887af1c4726d1 Mon Sep 17 00:00:00 2001 From: Hyewon Kang Date: Thu, 12 Dec 2024 21:22:28 +0900 Subject: [PATCH 310/388] Add Daum app user agent (#773) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Daum app user agent --------- Co-authored-by: helen.one(강혜원)/kakao --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 1 + test/data/ua/browser/browser-all.json | 20 ++++++++++++++++++++ 4 files changed, 23 insertions(+) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index f8cca69b4..db4ffa65a 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -29,6 +29,7 @@ export const Browser: Readonly<{ COBALT: "Cobalt"; COC_COC: "Coc Coc"; CONKEROR: "Conkeror"; + DAUM: "Daum"; DILLO: "Dillo"; DOLPHIN: "Dolphin"; DORIS: "Doris"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 67a5447b6..9e8dbe003 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -34,6 +34,7 @@ const Browser = Object.freeze({ COBALT: 'Cobalt', COC_COC: 'Coc Coc', CONKEROR: 'Conkeror', + DAUM: 'Daum', DILLO: 'Dillo', DOLPHIN: 'Dolphin', DORIS: 'Doris', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 620c4b038..0fc6214bb 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -420,6 +420,7 @@ /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp + /(daum)apps[\/ ]([\w\.]+)/i, // Daum App /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay diff --git a/test/data/ua/browser/browser-all.json b/test/data/ua/browser/browser-all.json index 45a8e0789..05dfd4fce 100644 --- a/test/data/ua/browser/browser-all.json +++ b/test/data/ua/browser/browser-all.json @@ -2503,6 +2503,26 @@ "type" : "inapp" } }, + { + "desc" : "Daum App Android", + "ua" : "Mozilla/5.0 (Linux; Android 11; SM-G970N Build/RP1A.200720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36 DaumApps/7.5.0 DaumDevice/mobile", + "expect" : { + "name" : "Daum", + "version": "7.5.0", + "major" : "7", + "type" : "inapp" + } + }, + { + "desc" : "Daum App iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Safari/605.1.15 Mobile/15E148 DaumApps/7.5.1 DaumDevice/mobile", + "expect" : { + "name" : "Daum", + "version": "7.5.1", + "major" : "7", + "type" : "inapp" + } + }, { "desc" : "TikTok", "ua" : "Mozilla/5.0 (Linux; Android 11; 21061119AG Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile Safari/537.36 trill_2022109040 JsSdk/1.0 NetType/MOBILE Channel/googleplay AppName/musical_ly app_version/21.9.4 ByteLocale/ru-RU ByteFullLocale/ru-RU Region/KG BytedanceWebview/d8a21c6", From de871b3d37f5022d20b688996c0b8c6051a46812 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Dec 2024 19:04:03 +0700 Subject: [PATCH 311/388] [test] Move test file for Windows IoT --- test/data/ua/os/windows-iot.json | 11 +++++++++++ test/data/ua/os/windows.json | 9 --------- 2 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 test/data/ua/os/windows-iot.json diff --git a/test/data/ua/os/windows-iot.json b/test/data/ua/os/windows-iot.json new file mode 100644 index 000000000..ff1d02c05 --- /dev/null +++ b/test/data/ua/os/windows-iot.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Windows IoT", + "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", + "expect" : + { + "name" : "Windows IoT", + "version" : "10.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json index 7e1228226..39e954155 100644 --- a/test/data/ua/os/windows.json +++ b/test/data/ua/os/windows.json @@ -116,15 +116,6 @@ "version" : "CE" } }, - { - "desc" : "Windows IoT", - "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", - "expect" : - { - "name" : "Windows IoT", - "version" : "10.0" - } - }, { "desc" : "Windows NT on x86 or aarch64 CPU using Firefox", "ua" : "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0", From 9cdf560e60c62ccd968b03bfe95b05b696a0a114 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Dec 2024 20:01:58 +0700 Subject: [PATCH 312/388] Improve detection for Nvidia devices --- src/main/ua-parser.js | 11 ++++++---- test/data/ua/device/nvidia.json | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0fc6214bb..5981dea81 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -89,6 +89,7 @@ LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', + NVIDIA = 'Nvidia', ONEPLUS = 'OnePlus', OPPO = 'OPPO', SAMSUNG = 'Samsung', @@ -736,8 +737,8 @@ ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [ /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [ - /(shield[\w ]+) b/i // Nvidia Shield Tablets - ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [ + /((?:tegranote|shield t(?!.+d tv))[\w- ]*?)(?: b|\))/i // Nvidia Tablets + ], [MODEL, [VENDOR, NVIDIA], [TYPE, TABLET]], [ /(sprint) (\w+)/i // Sprint Phones ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /(kin\.[onetw]{3})/i // Microsoft Kin @@ -769,6 +770,8 @@ ], [[MODEL, CHROMECAST], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ + /(shield \w+ tv)/i // Nvidia Shield TV + ], [MODEL, [VENDOR, NVIDIA], [TYPE, SMARTTV]], [ /\(dtv[\);].+(aquos)/i, /(aquos-tv[\w ]+)\)/i // Sharp ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ @@ -791,8 +794,8 @@ /(ouya)/i, // Ouya /(nintendo) (\w+)/i // Nintendo ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ - /droid.+; (shield) bui/i // Nvidia - ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [ + /droid.+; (shield)( bui|\))/i // Nvidia Portable + ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ /(playstation \w+)/i // Playstation ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [ /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox diff --git a/test/data/ua/device/nvidia.json b/test/data/ua/device/nvidia.json index a78316e06..96a2afc98 100644 --- a/test/data/ua/device/nvidia.json +++ b/test/data/ua/device/nvidia.json @@ -1,4 +1,22 @@ [ + { + "desc": "Tegra Note 7", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TegraNote-P1640 Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Safari/537.36", + "expect": { + "vendor": "Nvidia", + "model": "TegraNote-P1640", + "type": "tablet" + } + }, + { + "desc": "Nvidia Shield", + "ua": "Mozilla/5.0 (Linux; Android 5.1; SHIELD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.74 Mobile Safari/537.36", + "expect": { + "vendor": "Nvidia", + "model": "SHIELD", + "type": "console" + } + }, { "desc": "Nvidia Shield Tablet", "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SHIELD Tablet Build/LVY48E; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/45.0.2454.19 Safari/537.36", @@ -7,5 +25,23 @@ "model": "SHIELD Tablet", "type": "tablet" } + }, + { + "desc": "Nvidia Shield Tablet K1", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SHIELD Tablet K1 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Safari/537.36", + "expect": { + "vendor": "Nvidia", + "model": "SHIELD Tablet K1", + "type": "tablet" + } + }, + { + "desc": "Nvidia Shield TV", + "ua": "Mozilla/5.0 (Linux; Android 11; SHIELD Android TV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36 ", + "expect": { + "vendor": "Nvidia", + "model": "SHIELD Android TV", + "type": "smarttv" + } } ] \ No newline at end of file From 13d069f1c46ffb7edfeaa32d1e55b361350e9ed2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 12 Dec 2024 22:12:43 +0700 Subject: [PATCH 313/388] Improve device detection for unidentified SmartTV vendors --- src/main/ua-parser.js | 5 ++++- test/data/ua/device/_others.json | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 5981dea81..494dcbeb4 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -784,7 +784,10 @@ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors + // SmartTV from Unidentified Vendors + /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i + ], [MODEL, [TYPE, SMARTTV]], [ + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i ], [[TYPE, SMARTTV]], [ /////////////////// diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index b830065a6..b2bb7f192 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -34,6 +34,33 @@ "type": "mobile" } }, + { + "desc": "ChangHong Android TV", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn; ChangHong Android TV Build/LMY49J) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/10.8 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "ChangHong", + "type": "smarttv" + } + }, + { + "desc": "MStar Android TV", + "ua": "Mozilla/5.0 (Linux; Android 4.3.1; MStar Android TV Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.95 Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "MStar", + "type": "smarttv" + } + }, + { + "desc": "ONIDA Android TV", + "ua": "Mozilla/5.0 (Linux; Android 6.0; ONIDA Android TV Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/48.0.2542.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "ONIDA", + "type": "smarttv" + } + }, { "desc": "JVC LT-43V55LFA Smart TV", "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB211 VSTVB MB200 HbbTV/1.2.1 (; JVC; MB211; 3.19.4.2; _TV_NT72563_2017 SmartTvA/3.0.0", From 93552e0172459487f24bb0307e0e64ec5ce58cd6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 14 Dec 2024 21:16:32 +0700 Subject: [PATCH 314/388] Improve CPU detection: ARM --- src/main/ua-parser.js | 16 ++++++++-------- test/data/ua/cpu/cpu-all.json | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 494dcbeb4..0296329c8 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -495,30 +495,30 @@ cpu : [[ - /\b(?:(amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64) + /\b((amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64) ], [[ARCHITECTURE, 'amd64']], [ /(ia32(?=;))/i, // IA32 (quicktime) - /\b((?:i[346]|x)86)\b/i // IA32 (x86) + /\b((i[346]|x)86)\b/i // IA32 (x86) ], [[ARCHITECTURE, 'ia32']], [ - /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 + /\b(aarch64|arm(v?[89]e?l?|_?64))\b/i // ARM64 ], [[ARCHITECTURE, 'arm64']], [ - /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF + /\b(arm(v[67])?ht?n?[fl]p?)\b/i // ARMHF ], [[ARCHITECTURE, 'armhf']], [ // PocketPC mistakenly identified as PowerPC - /windows (ce|mobile); ppc;/i + /( (ce|mobile); ppc;|\/[\w\.]+arm\b)/i ], [[ARCHITECTURE, 'arm']], [ - /((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i // PowerPC + /((ppc|powerpc)(64)?)( mac|;|\))/i // PowerPC ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ - /(sun4\w)[;\)]/i // SPARC + / sun4\w[;\)]/i // SPARC ], [[ARCHITECTURE, 'sparc']], [ - /((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i + /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC ], [[ARCHITECTURE, lowerize]] ], diff --git a/test/data/ua/cpu/cpu-all.json b/test/data/ua/cpu/cpu-all.json index d43fbc0d6..5607eaf8e 100644 --- a/test/data/ua/cpu/cpu-all.json +++ b/test/data/ua/cpu/cpu-all.json @@ -175,6 +175,14 @@ "architecture" : "arm64" } }, + { + "desc" : "Google Search App", + "ua" : "Mozilla/5.0 (Linux; Android 9; JAT-LX1 Build/HONORJAT-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.96 Mobile Safari/537.36 GoogleApp/11.11.10.21.arm", + "expect" : + { + "architecture" : "arm" + } + }, { "desc" : "Google Search App", "ua" : "Mozilla/5.0 (Linux; Android 6.0; M5s Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.147 Mobile Safari/537.36 GSA/12.40.17.23.arm64", From 6c6ff97e0cdff8930cdd09c51667855a4272c1d9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 16 Dec 2024 11:05:24 +0700 Subject: [PATCH 315/388] Improve device detection for Generic device: capture its device model instead of its Android version --- src/main/ua-parser.js | 6 +++--- test/data/ua/device/_others.json | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0296329c8..73778c4aa 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -19,8 +19,8 @@ // Constants ///////////// - var LIBVERSION = '2.0.0', // UAParser.version - UA_MAX_LENGTH = 500, // UA string over this length will be trimmed + var LIBVERSION = '2.0.0', + UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', UNKNOWN = '?', @@ -864,7 +864,7 @@ ], [[TYPE, TABLET]], [ /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile ], [[TYPE, MOBILE]], [ - /(android[-\w\. ]{0,9});.+buil/i // Generic Android Device + /droid .+?; ([\w\. -]+)( bui|\))/i // Generic Android Device ], [MODEL, [VENDOR, 'Generic']] ], diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index b2bb7f192..53818da18 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -1,10 +1,18 @@ [ + { + "desc": "Generic Android Device", + "ua": "Dalvik/2.1.0 (Linux; U; Android 9; X96mini_RP Build/X96mini_RP)", + "expect": { + "vendor": "Generic", + "model": "X96mini_RP" + } + }, { "desc": "Generic Android Device", "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; i980 Build/MRA58K)", "expect": { "vendor": "Generic", - "model": "Android 6.0.1" + "model": "i980" } }, { From 852520fafe12d5df84811f9936e70de2be5bad88 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 16 Dec 2024 23:41:19 +0700 Subject: [PATCH 316/388] Also provide minified .mjs files of main module in /dist --- dist/ua-parser.min.mjs | 4 ++++ dist/ua-parser.pack.mjs | 4 ++++ package-lock.json | 8 ++++---- package.json | 2 +- script/build-dist.sh | 12 +++++++++++- 5 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 dist/ua-parser.min.mjs create mode 100644 dist/ua-parser.pack.mjs diff --git a/dist/ua-parser.min.mjs b/dist/ua-parser.min.mjs new file mode 100644 index 000000000..8c8fd1932 --- /dev/null +++ b/dist/ua-parser.min.mjs @@ -0,0 +1,4 @@ +/* UAParser.js v2.0.0 + Copyright © 2012-2024 Faisal Salman + AGPLv3 License */ +var LIBVERSION="2.0.0",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",SPACE=" ",HYPHEN="-",UNKNOWN="?",RGX_LOWDASH=/_/g,FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",BROWSER="browser",CPU="cpu",DEVICE="device",ENGINE="engine",OS="os",RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER=PLATFORM+"Version",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+HYPHEN+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+HYPHEN+MOBILE,CH_HEADER_MODEL=CH_HEADER+HYPHEN+MODEL,CH_HEADER_PLATFORM=CH_HEADER+HYPHEN+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST=CHROME+"cast",EDGE="Edge",FIREFOX="Firefox",HEADLESS="Headless",OPERA="Opera",FACEBOOK="Facebook",SAFARI="Safari",SOGOU="Sogou",WEBVIEW="WebView",ARM="arm",ANDROID="Android",WINDOWS="Windows",Browser="Browser",Mobile="Mobile",Tablet="Tablet",SMART="Smart",PREFIX_MOBILE=Mobile+SPACE,SUFFIX_BROWSER=SPACE+Browser;var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=stripQuotes(header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(BROWSER,detect(BROWSER)).set(CPU,detect(CPU)).set(DEVICE,detect(DEVICE)).set(ENGINE,detect(ENGINE)).set(OS,detect(OS))}}return this};this.parseUA=function(){if(this.itemType!=RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case BROWSER:case ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:GOOGLE+SPACE+CHROME,Edge:MICROSOFT+SPACE+EDGE,"Chrome WebView":ANDROID+SPACE+WEBVIEW,"Chrome Headless":HEADLESS+CHROME});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,ANDROID+" 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(BROWSER,parse(BROWSER)).set(CPU,parse(CPU)).set(DEVICE,parse(DEVICE)).set(ENGINE,parse(ENGINE)).set(OS,parse(OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(BROWSER,this.getBrowser()).set(CPU,this.getCPU()).set(DEVICE,this.getDevice()).set(ENGINE,this.getEngine()).set(OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(BROWSER)],["getCPU",createItemFunc(CPU)],["getDevice",createItemFunc(DEVICE)],["getEngine",createItemFunc(ENGINE)],["getOS",createItemFunc(OS)],["getResult",createItemFunc(RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file diff --git a/dist/ua-parser.pack.mjs b/dist/ua-parser.pack.mjs new file mode 100644 index 000000000..2891557f9 --- /dev/null +++ b/dist/ua-parser.pack.mjs @@ -0,0 +1,4 @@ +/* UAParser.js v2.0.0 + Copyright © 2012-2024 Faisal Salman + AGPLv3 License */ +function U(i){for(var e={},t=0;t{var t,o={},r=e;if(!Hi(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Vi,e):Vi,P.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Ni(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Ni(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return ji(i)&&(r=i.length>M?Ui(i,M):i),this}]]).setUA(r),this):new E(i,e,t).getResult()}E.VERSION="2.0.0",E.BROWSER=U([v,y,B,k]),E.CPU=U([C]),E.DEVICE=U([S,x,k,G,_,t,r,o,D]),E.ENGINE=E.OS=U([v,y]);export{E as UAParser}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b602a640f..118888a3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", - "uglify-js": "~3.12.0" + "uglify-js": "~3.19.3" }, "engines": { "node": "*" @@ -4265,9 +4265,9 @@ ] }, "node_modules/uglify-js": { - "version": "3.12.8", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", - "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, "bin": { "uglifyjs": "bin/uglifyjs" diff --git a/package.json b/package.json index 76c928824..ca3208ac7 100755 --- a/package.json +++ b/package.json @@ -229,7 +229,7 @@ "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", - "uglify-js": "~3.12.0" + "uglify-js": "~3.19.3" }, "repository": { "type": "git", diff --git a/script/build-dist.sh b/script/build-dist.sh index 884f4b423..0648a12df 100755 --- a/script/build-dist.sh +++ b/script/build-dist.sh @@ -4,10 +4,20 @@ SRC_PATH="src/main/ua-parser.js" MIN_PATH="dist/ua-parser.min.js" PACK_PATH="dist/ua-parser.pack.js" +SRC_PATH_MJS="src/main/ua-parser.mjs" +MIN_PATH_MJS="dist/ua-parser.min.mjs" +PACK_PATH_MJS="dist/ua-parser.pack.mjs" + # minified echo "Generate ${MIN_PATH}" uglifyjs $SRC_PATH -o $MIN_PATH --comments "/^ UA/" +echo "Generate ${MIN_PATH_MJS}" +uglifyjs $SRC_PATH_MJS -o $MIN_PATH_MJS --comments "/^ UA/" --module + # packed echo "Generate ${PACK_PATH}" -uglifyjs $SRC_PATH -o $PACK_PATH --comments "/^ UA/" --compress --mangle \ No newline at end of file +uglifyjs $SRC_PATH -o $PACK_PATH --comments "/^ UA/" --compress --mangle + +echo "Generate ${PACK_PATH_MJS}" +uglifyjs $SRC_PATH_MJS -o $PACK_PATH_MJS --comments "/^ UA/" --compress --mangle --module \ No newline at end of file From 20e874085b184779103f026ff107c4605398f2ed Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 18 Dec 2024 23:14:41 +0700 Subject: [PATCH 317/388] Improve device detection for unknown VR device --- src/main/ua-parser.js | 6 ++---- test/data/ua/device/_others.json | 8 ++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 73778c4aa..2938b07a5 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -856,10 +856,8 @@ // MIXED (GENERIC) /////////////////// - /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors - ], [MODEL, [TYPE, MOBILE]], [ - /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors - ], [MODEL, [TYPE, TABLET]], [ + /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i + ], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [ /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet ], [[TYPE, TABLET]], [ /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index 53818da18..c47da246c 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -24,6 +24,14 @@ "type": "mobile" } }, + { + "desc": "Unknown VR Device", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; Unknown Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile VR Safari/537.36", + "expect": { + "model": "Unknown", + "type": "xr" + } + }, { "desc": "Desktop (IE11 with Tablet string)", "ua": "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; GWX:MANAGED; rv:11.0) like Gecko", From e8a301265a0d2e903fce795a14fd1b61e8464a28 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 21 Dec 2024 12:53:06 +0700 Subject: [PATCH 318/388] [extensions] Create a new list for `Vehicles` user-agent: Volvo, Rivian, BYD --- src/extensions/ua-parser-extensions.d.ts | 3 ++- src/extensions/ua-parser-extensions.js | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index d1d0a12a3..a90a4437e 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -12,4 +12,5 @@ export const Emails: UAParserExt; export const Fetchers: UAParserExt; export const InApps: UAParserExt; export const Libraries: UAParserExt; -export const MediaPlayers: UAParserExt; \ No newline at end of file +export const MediaPlayers: UAParserExt; +export const Vehicles: UAParserExt; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 09b793c61..8f3043c3e 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -321,6 +321,24 @@ const Libraries = Object.freeze({ ] }); +///////////// +// VEHICLES +//////////// + +const Vehicles = Object.freeze({ + device : [ + [ + /dilink.+(byd) auto/i, // BYD + ], [VENDOR], [ + + /(rivian) (r1t)/i, // Rivian + ], [VENDOR, MODEL], [ + + /vcc.+netfront/i, // Volvo + ], [[VENDOR, 'Volvo']] + ] +}); + ////////// // BOTS ///////// @@ -343,5 +361,6 @@ module.exports = { Fetchers, InApps, Libraries, - MediaPlayers + MediaPlayers, + Vehicles }; \ No newline at end of file From 10ab810568db032035cd11822fd8ef58c74fbe18 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 23 Dec 2024 13:30:16 +0700 Subject: [PATCH 319/388] Improve detection for Nokia device & Symbian OS --- src/main/ua-parser.js | 13 ++++----- test/data/ua/device/nokia.json | 45 ++++++++++++++++++++++++++++++ test/data/ua/os/maemo.json | 18 ++++++++++++ test/data/ua/os/qnx.json | 2 +- test/data/ua/os/symbian.json | 51 ++++++++++++++++++++++++++++++++-- 5 files changed, 117 insertions(+), 12 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 2938b07a5..d7eedfa76 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -618,8 +618,8 @@ // Nokia /(nokia) (t[12][01])/i ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(?:maemo|nokia).*(n900|lumia \d+)/i, - /nokia[-_ ]?(([-\w\.]*))/i + /(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i, + /nokia[-_ ]?(([-\w\. ]*))/i ], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [ // Google @@ -930,15 +930,12 @@ ], [VERSION, NAME], [ /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ - // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS/OpenHarmony - /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish|openharmony)[-\/ ]?([\w\.]*)/i, - /(blackberry)\w*\/([\w\.]*)/i, // Blackberry - /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS - /\((series40);/i // Series 40 + // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/; ]?([\d\.]*)/i ], [NAME, VERSION], [ /\(bb(10);/i // BlackBerry 10 ], [VERSION, [NAME, BLACKBERRY]], [ - /(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i // Symbian + /(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i // Symbian ], [VERSION, [NAME, 'Symbian']], [ /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS ], [VERSION, [NAME, FIREFOX+' OS']], [ diff --git a/test/data/ua/device/nokia.json b/test/data/ua/device/nokia.json index c8cf6a929..375cfcc3f 100644 --- a/test/data/ua/device/nokia.json +++ b/test/data/ua/device/nokia.json @@ -17,6 +17,15 @@ "type": "mobile" } }, + { + "desc": "Nokia 5800 XpressMusic", + "ua": "Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Nokia5800d-1/10.4.016; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413", + "expect": { + "vendor": "Nokia", + "model": "5800d-1", + "type": "mobile" + } + }, { "desc": "Nokia 7", "ua": "Mozilla/5.0 (Linux; Android 11; Nokia 7.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", @@ -26,6 +35,33 @@ "type": "mobile" } }, + { + "desc": "Nokia 808 PureView", + "ua": "Mozilla/5.0 (Symbian; U; Nokia808 PureView; en-GB) AppleWebKit/534.3 (KHTML, like Gecko) Version/3.0 Mobile/1A543a Mobile Safari/534.3", + "expect": { + "vendor": "Nokia", + "model": "808 PureView", + "type": "mobile" + } + }, + { + "desc": "Nokia 808 PureView", + "ua": "Mozilla/5.0 (Symbian/3; Series60/5.5 Nokia808PureView/113.010.1508; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/535.1 (KHTML, like Gecko) NokiaBrowser/8.3.2.21 Mobile Safari/535.1 3gpp-gba", + "expect": { + "vendor": "Nokia", + "model": "808PureView", + "type": "mobile" + } + }, + { + "desc": "Nokia Lumia 630", + "ua": "UCWEB/2.0 (Windows; U; wds 8.10; en-IN; NOKIA; RM-978_1046) U2/1.0.0 UCBrowser/4.2.0.524 U2/1.0.0 Mobile", + "expect": { + "vendor": "Nokia", + "model": "RM-978", + "type": "mobile" + } + }, { "desc": "Nokia N9", "ua": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", @@ -35,6 +71,15 @@ "type": "mobile" } }, + { + "desc": "Nokia N900", + "ua": "Mozilla/5.0 (Linux; Maemo 5.0; Nokia N900; Build/4.0.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Mobile Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "N900", + "type": "mobile" + } + }, { "desc": "Nokia T20", "ua": "Mozilla/5.0 (Linux; Android 12; Nokia T20) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", diff --git a/test/data/ua/os/maemo.json b/test/data/ua/os/maemo.json index 92dfe7895..9d898491f 100644 --- a/test/data/ua/os/maemo.json +++ b/test/data/ua/os/maemo.json @@ -7,5 +7,23 @@ "name" : "Maemo", "version" : "undefined" } + }, + { + "desc" : "Nokia N900 Linux mobile, on the Maemo browser", + "ua" : "Mozilla/5.0(X11; U; Linux armv7l; fr-FR; rv:1.9.2a1pre) Gecko/20091127 Firefox/3.5 Maemo Browser 1.5.6 RX-51 N900", + "expect" : + { + "name" : "Maemo", + "version" : "undefined" + } + }, + { + "desc" : "Nokia N900 Linux mobile, on the Maemo browser", + "ua" : "Mozilla/5.0 (Linux; Maemo 5.0; Nokia N900; Build/4.0.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Mobile Safari/537.36", + "expect" : + { + "name" : "Maemo", + "version" : "5.0" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/qnx.json b/test/data/ua/os/qnx.json index fcb3ed1c8..2817e488f 100644 --- a/test/data/ua/os/qnx.json +++ b/test/data/ua/os/qnx.json @@ -5,7 +5,7 @@ "expect" : { "name" : "QNX", - "version" : "x86pc" + "version" : "undefined" } } ] \ No newline at end of file diff --git a/test/data/ua/os/symbian.json b/test/data/ua/os/symbian.json index 24f509e25..0e61d0281 100644 --- a/test/data/ua/os/symbian.json +++ b/test/data/ua/os/symbian.json @@ -1,6 +1,6 @@ [ { - "desc" : "Symbian", + "desc" : "Nokia 5250", "ua" : "Nokia5250/10.0.011 (SymbianOS/9.4; U; Series60/5.0 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/525 (KHTML, like Gecko) Safari/525 3gpp-gba", "expect" : { @@ -9,12 +9,57 @@ } }, { - "desc" : "Symbian", + "desc" : "Nokia N79", + "ua" : "Mozilla/5.0 (SymbianOS/9.3; U; Series60/3.2 NokiaN79-1/32.001; Profile/MIDP-2.1 Configuration/CLDC-1", + "expect" : + { + "name" : "Symbian", + "version" : "9.3" + } + }, + { + "desc" : "Nokia E71", + "ua" : "Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 NokiaE71-1/110.07.127; Profile/MIDP-2.0 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413", + "expect" : + { + "name" : "Symbian", + "version" : "9.2" + } + }, + { + "desc" : "Opera Mini on S60", + "ua" : "Opera/9.80 (Series 60; Opera Mini/7.1.32444/191.361; U; de) Presto/2.12.423 Version/12.16", + "expect" : + { + "name" : "Symbian", + "version" : "undefined" + } + }, + { + "desc" : "NokiaBrowser on Nokia C7", "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaC7-00/024.001; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.37 Mobile Safari/533.4 3gpp-gba", "expect" : { "name" : "Symbian", - "version" : "5.2" + "version" : "3" + } + }, + { + "desc" : "Nokia 808 PureView", + "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.5 Nokia808PureView/113.010.1508; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/535.1 (KHTML, like Gecko) NokiaBrowser/8.3.2.21 Mobile Safari/535.1 3gpp-gba", + "expect" : + { + "name" : "Symbian", + "version" : "3" + } + }, + { + "desc" : "Nokia 808 PureView", + "ua" : "Mozilla/5.0 (Symbian; U; Nokia808 PureView; en-GB) AppleWebKit/534.3 (KHTML, like Gecko) Version/3.0 Mobile/1A543a Mobile Safari/534.3", + "expect" : + { + "name" : "Symbian", + "version" : "undefined" } } ] \ No newline at end of file From 9e64f34e726d79c8ad2aca4bf4ed3cf989680285 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 23 Dec 2024 15:19:35 +0700 Subject: [PATCH 320/388] Improve CPU detection: x86 --- src/main/ua-parser.js | 2 +- test/data/ua/cpu/cpu-all.json | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d7eedfa76..a5e8d63d7 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -499,7 +499,7 @@ ], [[ARCHITECTURE, 'amd64']], [ /(ia32(?=;))/i, // IA32 (quicktime) - /\b((i[346]|x)86)\b/i // IA32 (x86) + /\b((i[346]|x)86)(pc)?\b/i // IA32 (x86) ], [[ARCHITECTURE, 'ia32']], [ /\b(aarch64|arm(v?[89]e?l?|_?64))\b/i // ARM64 diff --git a/test/data/ua/cpu/cpu-all.json b/test/data/ua/cpu/cpu-all.json index 5607eaf8e..a4f68b1b6 100644 --- a/test/data/ua/cpu/cpu-all.json +++ b/test/data/ua/cpu/cpu-all.json @@ -278,5 +278,13 @@ { "architecture" : "68k" } + }, + { + "desc" : "x86", + "ua" : "Mozilla/5.0 (Photon; U; QNX x86pc; en-US; rv:1.8.1.20) Gecko/20090127 BonEcho/2.0.0.20", + "expect" : + { + "architecture" : "ia32" + } } ] From 3ca23193ddc036261b41de75f2263596314641bf Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 23 Dec 2024 15:29:50 +0700 Subject: [PATCH 321/388] Improve OS detection: fix Linux arch mistakenly detected as version --- src/main/ua-parser.js | 2 +- test/data/ua/os/linux.json | 11 ++++++++++- test/unit/ua-ch.js | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a5e8d63d7..a449ada82 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -965,7 +965,7 @@ /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire - /(hurd|linux) ?([\w\.]*)/i, // Hurd/Linux + /(hurd|linux)(?: arm\w*| x86\w*| ?)([\w\.]*)/i, // Hurd/Linux /(gnu) ?([\w\.]*)/i, // GNU /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly /(haiku) (\w+)/i // Haiku diff --git a/test/data/ua/os/linux.json b/test/data/ua/os/linux.json index ad86c4e87..af7859363 100644 --- a/test/data/ua/os/linux.json +++ b/test/data/ua/os/linux.json @@ -5,7 +5,16 @@ "expect" : { "name" : "Linux", - "version" : "x86_64" + "version" : "undefined" + } + }, + { + "desc" : "Linux", + "ua" : "Mozilla/5.0 (X11; U; Linux armv61; en-US; rv:1.9.1b2pre) Gecko/20081015 Fennec/1.0a1", + "expect" : + { + "name" : "Linux", + "version" : "undefined" } } ] \ No newline at end of file diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 1342a3563..71bfdea9f 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -68,7 +68,7 @@ describe('Map UA-CH headers', () => { assert.strictEqual(uap.engine.name, 'Blink'); assert.strictEqual(uap.engine.version, '110.0.0.0'); assert.strictEqual(uap.os.name, "Linux"); - assert.strictEqual(uap.os.version, "x86_64"); + assert.strictEqual(uap.os.version, undefined); }); it('Fallback to user-agent header when using `withClientHints()` but found no client hints-related headers', () => { @@ -90,7 +90,7 @@ describe('Map UA-CH headers', () => { assert.strictEqual(uap.engine.name, 'Blink'); assert.strictEqual(uap.engine.version, '110.0.0.0'); assert.strictEqual(uap.os.name, "Linux"); - assert.strictEqual(uap.os.version, "x86_64"); + assert.strictEqual(uap.os.version, undefined); }); it('Can detect Apple silicon from client hints data', () => { From 51dce55e650eab110f403c2b5e6114b460e52fcd Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 4 Feb 2025 21:49:28 +0700 Subject: [PATCH 322/388] Bump version `2.0.1` --- CHANGELOG.md | 40 ++++ dist/ua-parser.min.js | 4 +- dist/ua-parser.min.mjs | 4 +- dist/ua-parser.pack.js | 4 +- dist/ua-parser.pack.mjs | 4 +- package-lock.json | 4 +- package.json | 2 +- src/enums/ua-parser-enums.d.ts | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 5 +- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 173 +++++++---------- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 2 +- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 226 +++++++++++++++-------- 19 files changed, 276 insertions(+), 210 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5545bafac..48b23eb7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,37 @@ --- +## Version 2.0.1 + +- Add new browser: Ladybird, Daum +- Add new device: Apple HomePod +- Add new device vendor: HMD +- Add new OS: Ubuntu Touch, Windows IoT +- Improve CPU detection: ARM, x86 +- Improve device detection: Lenovo, Nokia, Nvidia, Xiaomi + - Tablet: Google, Honor, Huawei, Infinix, Nokia, OnePlus, Xiaomi + - Wearable: Asus, Google, LG, Motorola, OnePlus, Oppo, Samsung, Sony + - Smart-TV: Xiaomi, unidentified vendors + - Improve detection for unknown VR devices + - Improve device model detection for Generic devices +- Improve OS detection: Linux, Symbian +- Improve TypeScript definitions for Headers +- Improve `withClientHints()`: + - `engine.version` also get updated + - Infer `device.vendor` & `device.type` by guessing from `device.model` + - Browser naming adjustments: + - `Google Chrome` => `Chrome` + - `Microsoft Edge` => `Edge` + - `Android WebView` => `Chrome WebView` + - `HeadlessChrome` => `Chrome Headless` +- `enums` submodule: + - Add TypeScript definitions +- `extensions` submodule: + - Add new list: + - `Vehicles`: BYD, Rivian, Volvo + - Add new Fetcher: Bluesky + - Add new Library: Apache-HttpClient, go-http-client, got, GuzzleHttp, Java-http-client, libwww-perl, lua-resty-http, Needle, OkHttp, node-fetch, PHP-SOAP, PostmanRuntime, superagent + ## Version 2.0.0 - `ua-parser-js/extensions` submodule: @@ -166,6 +197,15 @@ --- +## Version 0.7.40 / 1.0.40 +- Add new browser: 115, LibreWolf, Slimboat, Slimjet +- Add new device: Advan, Cat, Energizer, IMO, Micromax, Smartfren +- Add new engine: ArkWeb, Servo +- Add new os: OpenHarmony +- Improve browser detection: 2345, 360, Dragon, Iron, Maxthon +- Recognize Honor as a separate device vendor from Huawei +- Fix Python Request mistakenly identified as Meta Quest + ## Version 0.7.39 / 1.0.39 - Add new feature: executable command using `npx ua-parser-js "[INSERT-UA-HERE]"` - Add new browser: Helio, Pico Browser, Wolvic diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 54ff4b67d..21e380c2f 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0 +/* UAParser.js v2.0.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.0",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",USER_AGENT="user-agent",UA_MAX_LENGTH=500,BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",CHROME="Chrome",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=strip(/(Google|Microsoft) /,brands[i].brand||brands[i]),brandVersion=brands[i].version;if(!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&!/chromi/i.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL])}if(uaCH[MODEL]=="Xbox"){this.set(TYPE,CONSOLE).set(VENDOR,MICROSOFT)}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.1",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.min.mjs b/dist/ua-parser.min.mjs index 8c8fd1932..0d0cdbcce 100644 --- a/dist/ua-parser.min.mjs +++ b/dist/ua-parser.min.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0 +/* UAParser.js v2.0.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -var LIBVERSION="2.0.0",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",SPACE=" ",HYPHEN="-",UNKNOWN="?",RGX_LOWDASH=/_/g,FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",BROWSER="browser",CPU="cpu",DEVICE="device",ENGINE="engine",OS="os",RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER=PLATFORM+"Version",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+HYPHEN+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+HYPHEN+MOBILE,CH_HEADER_MODEL=CH_HEADER+HYPHEN+MODEL,CH_HEADER_PLATFORM=CH_HEADER+HYPHEN+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST=CHROME+"cast",EDGE="Edge",FIREFOX="Firefox",HEADLESS="Headless",OPERA="Opera",FACEBOOK="Facebook",SAFARI="Safari",SOGOU="Sogou",WEBVIEW="WebView",ARM="arm",ANDROID="Android",WINDOWS="Windows",Browser="Browser",Mobile="Mobile",Tablet="Tablet",SMART="Smart",PREFIX_MOBILE=Mobile+SPACE,SUFFIX_BROWSER=SPACE+Browser;var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=stripQuotes(header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(BROWSER,detect(BROWSER)).set(CPU,detect(CPU)).set(DEVICE,detect(DEVICE)).set(ENGINE,detect(ENGINE)).set(OS,detect(OS))}}return this};this.parseUA=function(){if(this.itemType!=RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case BROWSER:case ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:GOOGLE+SPACE+CHROME,Edge:MICROSOFT+SPACE+EDGE,"Chrome WebView":ANDROID+SPACE+WEBVIEW,"Chrome Headless":HEADLESS+CHROME});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,ANDROID+" 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(BROWSER,parse(BROWSER)).set(CPU,parse(CPU)).set(DEVICE,parse(DEVICE)).set(ENGINE,parse(ENGINE)).set(OS,parse(OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(BROWSER,this.getBrowser()).set(CPU,this.getCPU()).set(DEVICE,this.getDevice()).set(ENGINE,this.getEngine()).set(OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(BROWSER)],["getCPU",createItemFunc(CPU)],["getDevice",createItemFunc(DEVICE)],["getEngine",createItemFunc(ENGINE)],["getOS",createItemFunc(OS)],["getResult",createItemFunc(RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file +var LIBVERSION="2.0.1",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 6f7b1fb00..0d56ca468 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0 +/* UAParser.js v2.0.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -!function(i,d){"use strict";function e(i){for(var e={},t=0;tC?Ci(i,C):i),this}]]).setUA(r),this}Ui.VERSION="2.0.0",Ui.BROWSER=e([m,v,p,f]),Ui.CPU=e([k]),Ui.DEVICE=e([h,g,f,x,y,t,r,o,s]),Ui.ENGINE=Ui.OS=e([m,v]),typeof exports!==b?(typeof module!==b&&module.exports&&(exports=module.exports=Ui),exports.UAParser=Ui):typeof define===u&&define.amd?define(function(){return Ui}):li&&(i.UAParser=Ui);var ji,Ei=li&&(i.jQuery||i.Zepto);Ei&&!Ei.ua&&(ji=new Ui,Ei.ua=ji.getResult(),Ei.ua.get=function(){return ji.getUA()},Ei.ua.set=function(i){ji.setUA(i);var e,t=ji.getResult();for(e in t)Ei.ua[e]=t[e]})}("object"==typeof window?window:this); \ No newline at end of file +((i,l)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Pi,e):Pi,N.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Ri(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Ri(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>M?Hi(i,M):i),this}]]).setUA(r),this):new P(i,e,t).getResult()}P.VERSION="2.0.1",P.BROWSER=I([v,y,B,k]),P.CPU=I([C]),P.DEVICE=I([T,x,k,G,S,e,r,t,D]),P.ENGINE=P.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=P:exports).UAParser=P:typeof define===V&&define.amd?define(function(){return P}):Ti&&(i.UAParser=P);var Vi,Li=Ti&&(i.jQuery||i.Zepto);Li&&!Li.ua&&(Vi=new P,Li.ua=Vi.getResult(),Li.ua.get=function(){return Vi.getUA()},Li.ua.set=function(i){Vi.setUA(i);var e,t=Vi.getResult();for(e in t)Li.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.mjs b/dist/ua-parser.pack.mjs index 2891557f9..949ccc2ad 100644 --- a/dist/ua-parser.pack.mjs +++ b/dist/ua-parser.pack.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.0 +/* UAParser.js v2.0.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -function U(i){for(var e={},t=0;t{var t,o={},r=e;if(!Hi(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Vi,e):Vi,P.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Ni(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Ni(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return ji(i)&&(r=i.length>M?Ui(i,M):i),this}]]).setUA(r),this):new E(i,e,t).getResult()}E.VERSION="2.0.0",E.BROWSER=U([v,y,B,k]),E.CPU=U([C]),E.DEVICE=U([S,x,k,G,_,t,r,o,D]),E.ENGINE=E.OS=U([v,y]);export{E as UAParser}; \ No newline at end of file +function M(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ei,e):Ei,N.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Ri(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Ri(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return A(i)&&(r=i.length>P?Ai(i,P):i),this}]]).setUA(r),this):new I(i,e,t).getResult()}I.VERSION="2.0.1",I.BROWSER=M([g,x,C,v]),I.CPU=M([y]),I.DEVICE=M([T,k,v,G,S,i,r,e,F]),I.ENGINE=I.OS=M([g,x]);export{I as UAParser}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 118888a3c..385c237d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.0", + "version": "2.0.1", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index ca3208ac7..5fae0219a 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.0", + "version": "2.0.1", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index db4ffa65a..b1591bccd 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Enums submodule of UAParser.js v2.0.0 +// Type definitions for Enums submodule of UAParser.js v2.0.1 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 9e8dbe003..cbc3383fe 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0 +/* Enums for UAParser.js v2.0.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 276b8a268..171047732 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.0 +/* Enums for UAParser.js v2.0.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -38,6 +38,7 @@ const Browser = Object.freeze({ COBALT: 'Cobalt', COC_COC: 'Coc Coc', CONKEROR: 'Conkeror', + DAUM: 'Daum', DILLO: 'Dillo', DOLPHIN: 'Dolphin', DORIS: 'Doris', @@ -370,11 +371,13 @@ const OS = Object.freeze({ SYMBIAN: 'Symbian', TIZEN: 'Tizen', UBUNTU: 'Ubuntu', + UBUNTU_TOUCH: 'Ubuntu Touch', UNIX: 'Unix', VECTORLINUX: 'VectorLinux', WATCHOS: 'watchOS', WEBOS: 'WebOS', WINDOWS: 'Windows', + WINDOWS_IOT: 'Windows IoT', WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', XBOX: 'Xbox', diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index a90a4437e..f6ac5d315 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0 +// Type definitions for Helpers submodule of UAParser.js v2.0.1 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 8f3043c3e..a4fe34784 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0 +/* Extensions for UAParser.js v2.0.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 33f9b99a7..29a9e3923 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.0 +/* Extensions for UAParser.js v2.0.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -165,7 +165,7 @@ const ExtraDevices = Object.freeze({ /\b(zur\d{3}) b/i // Swiss ZUR Tablet ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ - /\b((zeki)?tb.*\b) b/i // Zeki Tablets + /^((zeki)?tb.*\b) b/i // Zeki Tablets ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ /\b([yr]\d{2}) b/i, @@ -222,31 +222,25 @@ const Fetchers = Object.freeze({ // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / Telegrambot / Twitterbot / UptimeRobot - /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot)\/([\w\.]+)/i, - - // Google Site Verifier - /(google-site-verification)\/([\w\.]+)/i, + // Google Site Verifier / Meta / Yahoo! Japan + // Yandex Bots - https://yandex.com/bots + /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, - // Meta - /(meta-externalfetcher)\/([\w\.]+)/i, + // Bluesky + /(bluesky) cardyb\/([\w\.]+)/i, // Slackbot - https://api.slack.com/robots /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, // WhatsApp - /(whatsapp)\/([\w\.]+)[\/ ][ianw]/i, - - // Yahoo! Japan - /(y!?j-dlc)\/([\w\.]+)/i, - - // Yandex Bots - https://yandex.com/bots - /(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, - /(yandex(?:sitelinks|userproxy))/i + /(whatsapp)\/([\w\.]+)[\/ ][ianw]/i ], [NAME, VERSION, [TYPE, FETCHER]], - // Google Bots / Cohere / Snapchat / Vercelbot - [/(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat)/i], + [ + // Google Bots / Cohere / Snapchat / Vercelbot / Yandex Bots + /(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat|yandex(?:sitelinks|userproxy))/i + ], [NAME, [TYPE, FETCHER]], ] }); @@ -271,104 +265,48 @@ const InApps = Object.freeze({ const MediaPlayers = Object.freeze({ browser : [[ - - /(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia - /(coremedia) v([\w\._]+)/i - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i, - // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC + /(apple(?:coremedia|tv))\/([\w\._]+)/i, // Generic Apple CoreMedia + /(coremedia) v([\w\._]+)/i, + // Ares/Nexplayer/OSSProxy + /(ares|clementine|music player daemon|nexplayer|ossproxy) ([\w\.-]+)/i, + // Aqualung/Lyssna/BSPlayer/Clementine/MPD + // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/GnomeMplayer/MoC // NSPlayer/PSP-InternetRadioPlayer/Videos - /(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD - /(lg player|nexplayer)\s([\d\.]+)/i, - /player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - /(nexplayer)\s([\w\.-]+)/i // Nexplayer + // Nero Home/Nero Scout/Nokia + // QuickTime/RealMedia/RadioApp/RadioClientApplication/ + // SoundTap/Totem/Stagefright/Streamium + // XBMC/gvfs/Xine/XMMS/irapp + /^(aqualung|audacious|audimusicstream|amarok|bass|bsplayer|core|gnomemplayer|gvfs|irapp|lyssna|music on console|nero (?:home|scout)|nokia\d+|nsplayer|psp-internetradioplayer|quicktime|rma|radioapp|radioclientapplication|soundtap|stagefright|streamium|totem|videos|xbmc|xine|xmms)\/([\w\.-]+)/i, + /(lg player|nexplayer) ([\d\.]+)/i, + /player\/(nexplayer|lg player) ([\w\.-]+)/i, // NexPlayer/LG Player + /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i, // Gstreamer + /(htc streaming player) [\w_]+ \/ ([\d\.]+)/i, // HTC Streaming Player + /(lavf)([\d\.]+)/i, // Lavf (FFMPEG) + // MPlayer SVN + /(mplayer)(?: |\/)(?:(?:sherpya-){0,1}svn)(?:-| )(r\d+(?:-\d+[\w\.-]+))/i, + / (songbird)\/([\w\.-]+)/i, // Songbird/Philips-Songbird + /(winamp)(?:3 version|mpeg| ) ([\w\.-]+)/i, // Winamp + /(vlc)(?:\/| media player - version )([\w\.-]+)/i, // VLC Videolan + /^(foobar2000|itunes|smp)\/([\d\.]+)/i, // Foobar2000/iTunes/SMP + /com\.(riseupradioalarm)\/([\d\.]*)/i, // RiseUP Radio Alarm + /(mplayer)(?:\s|\/| unknown-)([\w\.\-]+)/i, // MPlayer + // Windows Media Server + /(windows)\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ home media server/i ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ /(flrp)\/([\w\.-]+)/i // Flip Player ], [[NAME, 'Flip Player'], VERSION, [TYPE, MEDIAPLAYER]], [ - - /(fstream|nativehost|queryseekspider)/i // FStream/NativeHost/QuerySeekSpider + // MPlayer (no other info)/Media Player Classic/Nero ShowTime + // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) + // inlight radio / YourMuze + /(fstream|media player classic|inlight radio|mplayer|nativehost|nero showtime|ocms-bot|queryseekspider|tapinradio|tunein radio|winamp|yourmuze)/i ], [NAME, [TYPE, MEDIAPLAYER]], [ - /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i - // Gstreamer - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player - /(lavf)([\d\.]+)/i // Lavf (FFMPEG) - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(htc_one_s)\/([\d\.]+)/i, // HTC One S - ], [[NAME, /_/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ - - /(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i, - // MPlayer SVN - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer - /(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(mplayer)/i, // MPlayer (no other info) - /(yourmuze)/i, // YourMuze - /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime - ], [NAME, [TYPE, MEDIAPLAYER]], [ - - /(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(nokia\d+)\/([\w\.-]+)/i // Nokia - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(winamp)3 version ([\w\.-]+)/i, // Winamp - /(winamp)\s([\w\.-]+)/i, - /(winamp)mpeg\/([\w\.-]+)/i - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) - // inlight radio - ], [NAME, [TYPE, MEDIAPLAYER]], [ - - /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i - // QuickTime/RealMedia/RadioApp/RadioClientApplication/ - // SoundTap/Totem/Stagefright/Streamium - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(smp)([\d\.]+)/i // SMP - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ + /(htc_one_s|windows-media-player|wmplayer)\/([\w\.-]+)/i, // HTC One S / Windows Media Player + ], [[NAME, /[_-]/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ - /(vlc) media player - version ([\w\.]+)/i, // VLC Videolan - /(vlc)\/([\w\.-]+)/i, - /(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp - /(foobar2000)\/([\d\.]+)/i, // Foobar2000 - /(itunes)\/([\d\.]+)/i // iTunes - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(wmplayer)\/([\w\.-]+)/i, // Windows Media Player - /(windows-media-player)\/([\w\.-]+)/i - ], [[NAME, /-/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ - - /windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i, - // Windows Media Server - ], [VERSION, [NAME, 'Windows'], [TYPE, MEDIAPLAYER]], [ - - /(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm - ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ - - /(rad.io)\s([\d\.]+)/i, // Rad.io - /(radio.(?:de|at|fr))\s([\d\.]+)/i + /(rad.io|radio.(?:de|at|fr)) ([\d\.]+)/i // Rad.io ], [[NAME, 'rad.io'], VERSION, [TYPE, MEDIAPLAYER]] ] }); @@ -387,6 +325,24 @@ const Libraries = Object.freeze({ ] }); +///////////// +// VEHICLES +//////////// + +const Vehicles = Object.freeze({ + device : [ + [ + /dilink.+(byd) auto/i, // BYD + ], [VENDOR], [ + + /(rivian) (r1t)/i, // Rivian + ], [VENDOR, MODEL], [ + + /vcc.+netfront/i, // Volvo + ], [[VENDOR, 'Volvo']] + ] +}); + ////////// // BOTS ///////// @@ -409,5 +365,6 @@ export { Fetchers, InApps, Libraries, - MediaPlayers + MediaPlayers, + Vehicles }; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 86bfad170..bfa8f3738 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.0 +// Type definitions for Helpers submodule of UAParser.js v2.0.1 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 4af5175d3..66940d1c8 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0 +/* Helpers for UAParser.js v2.0.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index c8d166955..e3fb9a117 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.0 +/* Helpers for UAParser.js v2.0.1 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 03be66eea..17223f454 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.0 +// Type definitions for UAParser.js v2.0.1 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a449ada82..3b5e0a70a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0 +/* UAParser.js v2.0.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.0', + var LIBVERSION = '2.0.1', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 09dbce366..3027a551f 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.0 +/* UAParser.js v2.0.1 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,20 +21,35 @@ // Constants ///////////// - var LIBVERSION = '2.0.0', + var LIBVERSION = '2.0.1', + UA_MAX_LENGTH = 500, + USER_AGENT = 'user-agent', EMPTY = '', UNKNOWN = '?', + + // typeof FUNC_TYPE = 'function', UNDEF_TYPE = 'undefined', OBJ_TYPE = 'object', STR_TYPE = 'string', - MAJOR = 'major', - MODEL = 'model', + + // properties + UA_BROWSER = 'browser', + UA_CPU = 'cpu', + UA_DEVICE = 'device', + UA_ENGINE = 'engine', + UA_OS = 'os', + UA_RESULT = 'result', + NAME = 'name', TYPE = 'type', VENDOR = 'vendor', VERSION = 'version', ARCHITECTURE= 'architecture', + MAJOR = 'major', + MODEL = 'model', + + // device types CONSOLE = 'console', MOBILE = 'mobile', TABLET = 'tablet', @@ -42,9 +57,11 @@ WEARABLE = 'wearable', XR = 'xr', EMBEDDED = 'embedded', + + // browser types INAPP = 'inapp', - USER_AGENT = 'user-agent', - UA_MAX_LENGTH = 500, + + // client hints BRANDS = 'brands', FORMFACTORS = 'formFactors', FULLVERLIST = 'fullVersionList', @@ -61,12 +78,8 @@ CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], - UA_BROWSER = 'browser', - UA_CPU = 'cpu', - UA_DEVICE = 'device', - UA_ENGINE = 'engine', - UA_OS = 'os', - UA_RESULT = 'result', + + // device vendors AMAZON = 'Amazon', APPLE = 'Apple', ASUS = 'ASUS', @@ -78,20 +91,29 @@ LG = 'LG', MICROSOFT = 'Microsoft', MOTOROLA = 'Motorola', + NVIDIA = 'Nvidia', + ONEPLUS = 'OnePlus', + OPPO = 'OPPO', SAMSUNG = 'Samsung', SHARP = 'Sharp', SONY = 'Sony', XIAOMI = 'Xiaomi', ZEBRA = 'Zebra', - PREFIX_MOBILE = 'Mobile ', - SUFFIX_BROWSER = ' Browser', + + // browsers CHROME = 'Chrome', + CHROMIUM = 'Chromium', CHROMECAST = 'Chromecast', EDGE = 'Edge', FIREFOX = 'Firefox', OPERA = 'Opera', FACEBOOK = 'Facebook', SOGOU = 'Sogou', + + PREFIX_MOBILE = 'Mobile ', + SUFFIX_BROWSER = ' Browser', + + // os WINDOWS = 'Windows'; var isWindow = typeof window !== UNDEF_TYPE, @@ -401,6 +423,7 @@ /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp + /(daum)apps[\/ ]([\w\.]+)/i, // Daum App /safari (line)\/([\w\.]+)/i, // Line App for iOS /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay @@ -463,8 +486,8 @@ /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla // Other - /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, - // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser + /(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser/Ladybird /\b(links) \(([\w\.]+)/i // Links ], [NAME, [VERSION, /_/g, '.']], [ @@ -474,30 +497,30 @@ cpu : [[ - /\b(?:(amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64) + /\b((amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64) ], [[ARCHITECTURE, 'amd64']], [ /(ia32(?=;))/i, // IA32 (quicktime) - /((?:i[346]|x)86)[;\)]/i // IA32 (x86) + /\b((i[346]|x)86)(pc)?\b/i // IA32 (x86) ], [[ARCHITECTURE, 'ia32']], [ - /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 + /\b(aarch64|arm(v?[89]e?l?|_?64))\b/i // ARM64 ], [[ARCHITECTURE, 'arm64']], [ - /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF + /\b(arm(v[67])?ht?n?[fl]p?)\b/i // ARMHF ], [[ARCHITECTURE, 'armhf']], [ // PocketPC mistakenly identified as PowerPC - /windows (ce|mobile); ppc;/i + /( (ce|mobile); ppc;|\/[\w\.]+arm\b)/i ], [[ARCHITECTURE, 'arm']], [ - /((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i // PowerPC + /((ppc|powerpc)(64)?)( mac|;|\))/i // PowerPC ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ - /(sun4\w)[;\)]/i // SPARC + / sun4\w[;\)]/i // SPARC ], [[ARCHITECTURE, 'sparc']], [ - /((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i + /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC ], [[ARCHITECTURE, lowerize]] ], @@ -531,34 +554,38 @@ ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ // Honor - /(?:honor)([-\w ]+)[;\)]/i + /\b((?:brt|eln|hey2?|gdi|jdn)-a?[lnw]09|(?:ag[rm]3?|jdn2|kob2)-a?[lw]0[09]hn)(?: bui|\)|;)/i + ], [MODEL, [VENDOR, HONOR], [TYPE, TABLET]], [ + /honor([-\w ]+)[;\)]/i ], [MODEL, [VENDOR, HONOR], [TYPE, MOBILE]], [ // Huawei - /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i + /\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ /(?:huawei)([-\w ]+)[;\)]/i, /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ // Xiaomi + /oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i, + /\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets + ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ + /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models - /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i // Xiaomi Mi + /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i, // Xiaomi Mi + / ([\w ]+) miui\/v?\d/i ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ - /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad - /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets - ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ // OPPO /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i - ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ - /\b(opd2\d{3}a?) bui/i - ], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, OPPO], [TYPE, MOBILE]], [ + /\b(opd2(\d{3}a?))(?: bui|\))/i + ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [ // Vivo /vivo (\w+)(?: bui|\))/i, @@ -572,7 +599,7 @@ // Motorola /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, /\bmot(?:orola)?[- ](\w*)/i, - /((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i + /((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ /\b(mz60\d|xoom[2 ]{0,2}) build\//i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ @@ -581,22 +608,24 @@ /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, - /\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i, + /\blg[-e;\/ ]+((?!browser|netcast|android tv|watch)\w+)/i, /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ // Lenovo - /(ideatab[-\w ]+)/i, - /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i + /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, + /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ // Nokia - /(?:maemo|nokia).*(n900|lumia \d+)/i, - /nokia[-_ ]?([-\w\.]*)/i - ], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [ + /(nokia) (t[12][01])/i + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + /(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i, + /nokia[-_ ]?(([-\w\. ]*))/i + ], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [ // Google - /(pixel c)\b/i // Google Pixel C + /(pixel (c|tablet))\b/i // Google Pixel C/Tablet ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ @@ -611,7 +640,7 @@ // OnePlus / (kb2005|in20[12]5|be20[12][59])\b/i, /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i - ], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [ + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ // Amazon /(alexa)webm/i, @@ -687,18 +716,19 @@ ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // MIXED - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + /(imo) (tab \w+)/i, // IMO + /(infinix) (x1101b?)/i // Infinix XPad + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (imo) ((?!tab)[\w ]+?)(?: bui|\))/i, // IMO + /; (hmd|imo) ([\w ]+?)(?: bui|\))/i, // HMD/IMO /(hp) ([\w ]+\w)/i, // HP iPAQ - /(asus)-?(\w+)/i, // Asus /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia - /(lenovo)[-_ ]?([-\w]+)/i, // Lenovo - /(jolla)/i, // Jolla + /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ - /(imo) (tab \w+)/i, // IMO /(kobo)\s(ereader|touch)/i, // Kobo /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad @@ -709,8 +739,8 @@ ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [ /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [ - /(shield[\w ]+) b/i // Nvidia Shield Tablets - ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [ + /((?:tegranote|shield t(?!.+d tv))[\w- ]*?)(?: b|\))/i // Nvidia Tablets + ], [MODEL, [VENDOR, NVIDIA], [TYPE, TABLET]], [ /(sprint) (\w+)/i // Sprint Phones ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /(kin\.[onetw]{3})/i // Microsoft Kin @@ -742,19 +772,24 @@ ], [[MODEL, CHROMECAST], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ + /(shield \w+ tv)/i // Nvidia Shield TV + ], [MODEL, [VENDOR, NVIDIA], [TYPE, SMARTTV]], [ /\(dtv[\);].+(aquos)/i, /(aquos-tv[\w ]+)\)/i // Sharp ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ /(bravia[\w ]+)( bui|\))/i // Sony ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ - /(mitv-\w{5}) bui/i // Xiaomi + /(mi(tv|box)-?\w+) bui/i // Xiaomi ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ /Hbbtv.*(technisat) (.*);/i // TechniSAT ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors + // SmartTV from Unidentified Vendors + /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i + ], [MODEL, [TYPE, SMARTTV]], [ + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i ], [[TYPE, SMARTTV]], [ /////////////////// @@ -764,8 +799,8 @@ /(ouya)/i, // Ouya /(nintendo) (\w+)/i // Nintendo ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ - /droid.+; (shield) bui/i // Nvidia - ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [ + /droid.+; (shield)( bui|\))/i // Nvidia Portable + ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ /(playstation \w+)/i // Playstation ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [ /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox @@ -775,12 +810,23 @@ // WEARABLES /////////////////// - /\b(sm-[lr]\d\d[05][fnuw]?s?)\b/i // Samsung Galaxy Watch + /\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i // Samsung Galaxy Watch ], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [ - /((pebble))app/i // Pebble + /((pebble))app/i, // Pebble + /(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ + /(ow(?:19|20)?we?[1-3]{1,3})/i // Oppo Watch + ], [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]], [ /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ + /(opwwe\d{3})/i // OnePlus Watch + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, WEARABLE]], [ + /(moto 360)/i // Motorola 360 + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, WEARABLE]], [ + /(smartwatch 3)/i // Sony SmartWatch + ], [MODEL, [VENDOR, SONY], [TYPE, WEARABLE]], [ + /(g watch r)/i // LG G Watch R + ], [MODEL, [VENDOR, LG], [TYPE, WEARABLE]], [ /droid.+; (wt63?0{2,3})\)/i ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ @@ -803,20 +849,22 @@ ], [VENDOR, [TYPE, EMBEDDED]], [ /(aeobc)\b/i // Echo Dot ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ + /(homepod).+mac os/i // Apple HomePod + ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [ + /windows iot/i + ], [[TYPE, EMBEDDED]], [ //////////////////// // MIXED (GENERIC) /////////////////// - /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors - ], [MODEL, [TYPE, MOBILE]], [ - /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors - ], [MODEL, [TYPE, TABLET]], [ + /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i + ], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [ /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet ], [[TYPE, TABLET]], [ /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile ], [[TYPE, MOBILE]], [ - /(android[-\w\. ]{0,9});.+buil/i // Generic Android Device + /droid .+?; ([\w\. -]+)( bui|\))/i // Generic Android Device ], [MODEL, [VENDOR, 'Generic']] ], @@ -836,8 +884,11 @@ /ekioh(flow)\/([\w\.]+)/i, // Flow /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab - /\b(libweb)/i + + /\b(libweb)/i // LibWeb ], [NAME, VERSION], [ + /ladybird\//i + ], [[NAME, 'LibWeb']], [ /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko ], [VERSION, NAME] @@ -848,15 +899,15 @@ // Windows /microsoft (windows) (vista|xp)/i // Windows (iTunes) ], [NAME, VERSION], [ - /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i // Windows Phone + /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ - /windows nt 6\.2; (arm)/i, // Windows RT - /windows[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i, + /windows nt 6\.2; (arm)/i, // Windows RT + /windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i, /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ // iOS/macOS - /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS + /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i, /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ @@ -878,15 +929,15 @@ // Mobile OSes /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS - ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS/OpenHarmony - /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish|openharmony)[-\/ ]?([\w\.]*)/i, - /(blackberry)\w*\/([\w\.]*)/i, // Blackberry - /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS - /\((series40);/i // Series 40 + ], [VERSION, NAME], [ + /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch + ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ + // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/; ]?([\d\.]*)/i ], [NAME, VERSION], [ /\(bb(10);/i // BlackBerry 10 ], [VERSION, [NAME, BLACKBERRY]], [ - /(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i // Symbian + /(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i // Symbian ], [VERSION, [NAME, 'Symbian']], [ /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS ], [VERSION, [NAME, FIREFOX+' OS']], [ @@ -916,7 +967,7 @@ /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire - /(hurd|linux) ?([\w\.]*)/i, // Hurd/Linux + /(hurd|linux)(?: arm\w*| x86\w*| ?)([\w\.]*)/i, // Hurd/Linux /(gnu) ?([\w\.]*)/i, // GNU /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly /(haiku) (\w+)/i // Haiku @@ -1153,17 +1204,27 @@ switch (this.itemType) { case UA_BROWSER: + case UA_ENGINE: var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; if (brands) { for (var i in brands) { - var brandName = strip(/(Google|Microsoft) /, brands[i].brand || brands[i]), + var brandName = brands[i].brand || brands[i], brandVersion = brands[i].version; - if (!/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && !/chromi/i.test(brandName)))) { + if (this.itemType == UA_BROWSER && !/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && brandName != CHROMIUM))) { + brandName = strMapper(brandName, { + 'Chrome' : 'Google Chrome', + 'Edge' : 'Microsoft Edge', + 'Chrome WebView' : 'Android WebView', + 'Chrome Headless' : 'HeadlessChrome' + }); this.set(NAME, brandName) .set(VERSION, brandVersion) .set(MAJOR, majorize(brandVersion)); prevName = brandName; } + if (this.itemType == UA_ENGINE && brandName == CHROMIUM) { + this.set(VERSION, brandVersion); + } } } break; @@ -1180,11 +1241,16 @@ } if (uaCH[MODEL]) { this.set(MODEL, uaCH[MODEL]); - } - // Xbox-Specific Detection - if (uaCH[MODEL] == 'Xbox') { - this.set(TYPE, CONSOLE) - .set(VENDOR, MICROSOFT); + if (!this.get(TYPE) || !this.get(VENDOR)) { + var reParse = {}; + rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap); + if (!this.get(TYPE) && !!reParse.type) { + this.set(TYPE, reParse.type); + } + if (!this.get(VENDOR) && !!reParse.vendor) { + this.set(VENDOR, reParse.vendor); + } + } } if (uaCH[FORMFACTORS]) { var ff; From 98e0708f2543fdce20f35e5b9c34a43b09e1ed29 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Feb 2025 11:42:18 +0700 Subject: [PATCH 323/388] Fix issue #776 - Missing type def for node-fetch --- package-lock.json | 22 ++++++---------------- package.json | 5 ++--- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 385c237d3..dffa977c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ ], "license": "AGPL-3.0-or-later", "dependencies": { + "@types/node-fetch": "^2.6.12", "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", "ua-is-frozen": "^0.1.2" @@ -35,8 +36,6 @@ "@babel/traverse": "7.23.2", "@jazzer.js/core": "^1.4.0", "@playwright/test": "^1.49.0", - "@types/node": "^22.9.1", - "@types/node-fetch": "^2.6.12", "jshint": "~2.13.6", "mocha": "~8.2.0", "node-fetch": "^2.7.0", @@ -726,10 +725,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", - "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", - "dev": true, + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", "dependencies": { "undici-types": "~6.20.0" } @@ -738,7 +736,6 @@ "version": "2.6.12", "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "dev": true, "dependencies": { "@types/node": "*", "form-data": "^4.0.0" @@ -902,8 +899,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { "version": "1.7.8", @@ -1276,7 +1272,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1417,7 +1412,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -1750,7 +1744,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -2658,7 +2651,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -2667,7 +2659,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -4279,8 +4270,7 @@ "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, "node_modules/universalify": { "version": "2.0.1", diff --git a/package.json b/package.json index 5fae0219a..d0258e889 100755 --- a/package.json +++ b/package.json @@ -214,15 +214,14 @@ "dependencies": { "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", - "ua-is-frozen": "^0.1.2" + "ua-is-frozen": "^0.1.2", + "@types/node-fetch": "^2.6.12" }, "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.23.2", "@jazzer.js/core": "^1.4.0", "@playwright/test": "^1.49.0", - "@types/node": "^22.9.1", - "@types/node-fetch": "^2.6.12", "jshint": "~2.13.6", "mocha": "~8.2.0", "node-fetch": "^2.7.0", From 4ea03a587d65d0c8386054a75f10935a31371a05 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 9 Feb 2025 07:34:43 +0700 Subject: [PATCH 324/388] Bump version `2.0.2` --- CHANGELOG.md | 4 ++++ dist/ua-parser.min.js | 4 ++-- dist/ua-parser.min.mjs | 4 ++-- dist/ua-parser.pack.js | 4 ++-- dist/ua-parser.pack.mjs | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- src/enums/ua-parser-enums.d.ts | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 2 +- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 2 +- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 2 +- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 ++-- src/main/ua-parser.mjs | 4 ++-- 19 files changed, 29 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48b23eb7a..f62d6ac58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,10 @@ --- +## Version 2.0.2 + +- Fix TypeScript dependency issue + ## Version 2.0.1 - Add new browser: Ladybird, Daum diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 21e380c2f..02d8a82bc 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.1 +/* UAParser.js v2.0.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.1",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.2",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.min.mjs b/dist/ua-parser.min.mjs index 0d0cdbcce..6def7c8c6 100644 --- a/dist/ua-parser.min.mjs +++ b/dist/ua-parser.min.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.1 +/* UAParser.js v2.0.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -var LIBVERSION="2.0.1",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file +var LIBVERSION="2.0.2",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 0d56ca468..6807b0eac 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.1 +/* UAParser.js v2.0.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -((i,l)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Pi,e):Pi,N.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Ri(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Ri(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>M?Hi(i,M):i),this}]]).setUA(r),this):new P(i,e,t).getResult()}P.VERSION="2.0.1",P.BROWSER=I([v,y,B,k]),P.CPU=I([C]),P.DEVICE=I([T,x,k,G,S,e,r,t,D]),P.ENGINE=P.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=P:exports).UAParser=P:typeof define===V&&define.amd?define(function(){return P}):Ti&&(i.UAParser=P);var Vi,Li=Ti&&(i.jQuery||i.Zepto);Li&&!Li.ua&&(Vi=new P,Li.ua=Vi.getResult(),Li.ua.get=function(){return Vi.getUA()},Li.ua.set=function(i){Vi.setUA(i);var e,t=Vi.getResult();for(e in t)Li.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file +((i,l)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Pi,e):Pi,N.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Ri(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Ri(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>M?Hi(i,M):i),this}]]).setUA(r),this):new P(i,e,t).getResult()}P.VERSION="2.0.2",P.BROWSER=I([v,y,B,k]),P.CPU=I([C]),P.DEVICE=I([T,x,k,G,S,e,r,t,D]),P.ENGINE=P.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=P:exports).UAParser=P:typeof define===V&&define.amd?define(function(){return P}):Ti&&(i.UAParser=P);var Vi,Li=Ti&&(i.jQuery||i.Zepto);Li&&!Li.ua&&(Vi=new P,Li.ua=Vi.getResult(),Li.ua.get=function(){return Vi.getUA()},Li.ua.set=function(i){Vi.setUA(i);var e,t=Vi.getResult();for(e in t)Li.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.mjs b/dist/ua-parser.pack.mjs index 949ccc2ad..5cc94b76e 100644 --- a/dist/ua-parser.pack.mjs +++ b/dist/ua-parser.pack.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.1 +/* UAParser.js v2.0.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License */ -function M(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ei,e):Ei,N.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Ri(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Ri(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return A(i)&&(r=i.length>P?Ai(i,P):i),this}]]).setUA(r),this):new I(i,e,t).getResult()}I.VERSION="2.0.1",I.BROWSER=M([g,x,C,v]),I.CPU=M([y]),I.DEVICE=M([T,k,v,G,S,i,r,e,F]),I.ENGINE=I.OS=M([g,x]);export{I as UAParser}; \ No newline at end of file +function M(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ei,e):Ei,N.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Ri(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Ri(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return A(i)&&(r=i.length>P?Ai(i,P):i),this}]]).setUA(r),this):new I(i,e,t).getResult()}I.VERSION="2.0.2",I.BROWSER=M([g,x,C,v]),I.CPU=M([y]),I.DEVICE=M([T,k,v,G,S,i,r,e,F]),I.ENGINE=I.OS=M([g,x]);export{I as UAParser}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index dffa977c6..e5779852b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.1", + "version": "2.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.1", + "version": "2.0.2", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index d0258e889..d7399cf81 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.1", + "version": "2.0.2", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index b1591bccd..a6b1e59fb 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Enums submodule of UAParser.js v2.0.1 +// Type definitions for Enums submodule of UAParser.js v2.0.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index cbc3383fe..61a857ba3 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.1 +/* Enums for UAParser.js v2.0.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 171047732..6e8257aa6 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.1 +/* Enums for UAParser.js v2.0.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index f6ac5d315..500a0a2aa 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.1 +// Type definitions for Helpers submodule of UAParser.js v2.0.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index a4fe34784..9cd884cbf 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.1 +/* Extensions for UAParser.js v2.0.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 29a9e3923..8e7bc88fd 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.1 +/* Extensions for UAParser.js v2.0.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index bfa8f3738..389e8c675 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.1 +// Type definitions for Helpers submodule of UAParser.js v2.0.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 66940d1c8..3cd3aacbc 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.1 +/* Helpers for UAParser.js v2.0.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index e3fb9a117..622e27a62 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.1 +/* Helpers for UAParser.js v2.0.2 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 17223f454..c22a3de53 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.1 +// Type definitions for UAParser.js v2.0.2 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 3b5e0a70a..3e266ebd9 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.1 +/* UAParser.js v2.0.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.1', + var LIBVERSION = '2.0.2', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 3027a551f..0dfa143cf 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.1 +/* UAParser.js v2.0.2 Copyright © 2012-2024 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.1', + var LIBVERSION = '2.0.2', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', From 5413a9888d504ef7071b08d3fded6ac161cc73a4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 10 Feb 2025 00:55:32 +0700 Subject: [PATCH 325/388] Move node-fetch to dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d7399cf81..d6b0e8370 100755 --- a/package.json +++ b/package.json @@ -215,6 +215,7 @@ "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", "ua-is-frozen": "^0.1.2", + "node-fetch": "^2.7.0", "@types/node-fetch": "^2.6.12" }, "devDependencies": { @@ -224,7 +225,6 @@ "@playwright/test": "^1.49.0", "jshint": "~2.13.6", "mocha": "~8.2.0", - "node-fetch": "^2.7.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", From 6f0191c11b6ac5accd0499c977137b49b55d56cd Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 15 Feb 2025 13:37:56 +0700 Subject: [PATCH 326/388] [test] Move playwright installation inside `test:playwright` script --- .github/workflows/test-ci.yml | 1 - package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml index 0c46a4866..feb52062d 100644 --- a/.github/workflows/test-ci.yml +++ b/.github/workflows/test-ci.yml @@ -19,5 +19,4 @@ jobs: - name: Run the test run: | npm ci - npx playwright install --with-deps npm test diff --git a/package.json b/package.json index d6b0e8370..3ec26172c 100755 --- a/package.json +++ b/package.json @@ -209,7 +209,7 @@ "test:jshint": "jshint src/main", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", "test:mocha": "mocha test/unit", - "test:playwright": "playwright test test/e2e --browser all" + "test:playwright": "npx playwright install && playwright test test/e2e --browser all" }, "dependencies": { "detect-europe-js": "^0.1.2", From 837d31963033d937f72708e07e3e34673e7be884 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 20 Feb 2025 20:34:45 +0700 Subject: [PATCH 327/388] [extensions][helpers] Update Semrush bot variants --- package-lock.json | 10 +++------- src/extensions/ua-parser-extensions.js | 8 +++++--- src/helpers/ua-parser-helpers.js | 3 +++ test/data/ua/extension/crawler.json | 10 ++++++++++ test/data/ua/extension/fetcher.json | 10 ++++++++++ test/unit/helpers.js | 2 ++ 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index e5779852b..187bf0243 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@types/node-fetch": "^2.6.12", "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", + "node-fetch": "^2.7.0", "ua-is-frozen": "^0.1.2" }, "bin": { @@ -38,7 +39,6 @@ "@playwright/test": "^1.49.0", "jshint": "~2.13.6", "mocha": "~8.2.0", - "node-fetch": "^2.7.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", @@ -3144,7 +3144,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -4179,8 +4178,7 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/trim-newlines": { "version": "3.0.1", @@ -4336,14 +4334,12 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 9cd884cbf..1d528c9ee 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -52,7 +52,6 @@ const Crawlers = Object.freeze({ // MojeekBot - https://www.mojeek.com/bot.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot - // SemrushBot - http://www.semrush.com/bot.html // SeznamBot - http://napoveda.seznam.cz/seznambot-intro /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, @@ -78,6 +77,9 @@ const Crawlers = Object.freeze({ // Internet Archive (archive.org) /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + // SemrushBot - http://www.semrush.com/bot.html + /((?:semrush|splitsignal)bot[-abcfimostw]*)\/([\w\.-]+)/i, + // Sogou Spider /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, @@ -217,10 +219,10 @@ const Fetchers = Object.freeze({ // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // DuckAssistBot - https://duckduckgo.com/duckassistbot/ - // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / Telegrambot / Twitterbot / UptimeRobot + // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|telegram|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 3cd3aacbc..daaa89792 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -93,6 +93,9 @@ const isAIBot = (resultOrUA) => [ // Perplexity 'perplexitybot', + // Semrush + 'semrushbot-ocob', + // Timpi 'timpibot', diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 04a1a3525..48c586508 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -480,6 +480,16 @@ "type" : "crawler" } }, + { + "desc" : "SemrushBot for ContentShake AI tool", + "ua" : "Mozilla/5.0 (compatible; SemrushBot-OCOB/1; +https://www.semrush.com/bot/)", + "expect" : + { + "name" : "SemrushBot-OCOB", + "version" : "1", + "type" : "crawler" + } + }, { "desc" : "SeznamBot", "ua" : "Mozilla/5.0 (compatible; SeznamBot/4.0-RC1; +http://napoveda.seznam.cz/seznambot-intro/)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index a04e4f677..075281e87 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -119,6 +119,16 @@ "type" : "fetcher" } }, + { + "desc" : "SiteAuditBot", + "ua" : "Mozilla/5.0 (compatible; SiteAuditBot/0.97; +http://www.semrush.com/bot.html)", + "expect" : + { + "name" : "SiteAuditBot", + "version" : "0.97", + "type" : "fetcher" + } + }, { "desc" : "UptimeRobot", "ua" : "Mozilla/5.0 (compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)", diff --git a/test/unit/helpers.js b/test/unit/helpers.js index fcd667859..95ce87156 100644 --- a/test/unit/helpers.js +++ b/test/unit/helpers.js @@ -40,11 +40,13 @@ describe('isAIBot', () => { const claudeBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)'; const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; const searchGPT = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot'; + const semrushAI = 'Mozilla/5.0 (compatible; SemrushBot-OCOB/1; +https://www.semrush.com/bot/)'; assert.equal(isAIBot(UAParser(claudeBot, Bots)), true); assert.equal(isAIBot(claudeBot), true); assert.equal(isAIBot(firefox), false); assert.equal(isAIBot(searchGPT), true); + assert.equal(isAIBot(semrushAI), true); }); }); From d8057bcd8a037eb7521a91c9eaa20dc40875b88e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 21 Feb 2025 21:14:54 +0700 Subject: [PATCH 328/388] Browser naming adjustments for Client Hints: - `HuaweiBrowser` => `Huawei Browser`, - `Miui Browser` => `MIUI Browser`, - `OperaMobile` => `Opera Mobi`, - `YaBrowser` => `Yandex` --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 6 +- test/unit/ua-ch.js | 215 ++++++++++++++++++++++++++++++--- 4 files changed, 202 insertions(+), 21 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index a6b1e59fb..c279436cf 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -97,6 +97,7 @@ export const Browser: Readonly<{ OMNIWEB: "OmniWeb"; OPERA: "Opera"; OPERA_COAST: "Opera Coast"; + OPERA_GX: "Opera GX", OPERA_MINI: "Opera Mini"; OPERA_MOBI: "Opera Mobi"; OPERA_TABLET: "Opera Tablet"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 61a857ba3..25b8de520 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -102,6 +102,7 @@ const Browser = Object.freeze({ OMNIWEB: 'OmniWeb', OPERA: 'Opera', OPERA_COAST: 'Opera Coast', + OPERA_GX: 'Opera GX', OPERA_MINI: 'Opera Mini', OPERA_MOBI: 'Opera Mobi', OPERA_TABLET: 'Opera Tablet', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 3e266ebd9..31c1c3302 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1213,7 +1213,11 @@ 'Chrome' : 'Google Chrome', 'Edge' : 'Microsoft Edge', 'Chrome WebView' : 'Android WebView', - 'Chrome Headless' : 'HeadlessChrome' + 'Chrome Headless' : 'HeadlessChrome', + 'Huawei Browser' : 'HuaweiBrowser', + 'MIUI Browser' : 'Miui Browser', + 'Opera Mobi' : 'OperaMobile', + 'Yandex' : 'YaBrowser' }); this.set(NAME, brandName) .set(VERSION, brandVersion) diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 71bfdea9f..54e2f9a7c 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -220,45 +220,220 @@ describe('Map UA-CH headers', () => { }); }); -describe('Browser naming adjustments', () => { +describe('UA-CH Headers tests', () => { [ { - ua_ch: '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', + headers : { + 'sec-ch-ua': '"Avast Secure Browser";v="131", "Chromium";v="131", "Not_A Brand";v="24"' + }, expect: { - before: 'Google Chrome', - after: 'Chrome' + browser : { + name : 'Avast Secure Browser', + version : '131', + major : '131', + type : undefined + } } }, { - ua_ch: '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"', + headers : { + 'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Brave";v="132"' + }, expect: { - before: "Microsoft Edge", - after: "Edge" + browser : { + name : 'Brave', + version : '132', + major : '132', + type : undefined + } } }, { - ua_ch: '"Android WebView";v="123", "Not:A-Brand";v="8", "Chromium";v="123"', + headers : { + 'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"' + }, expect: { - before: "Android WebView", - after: "Chrome WebView" + browser : { + name : 'Chrome', + version : '111', + major : '111', + type : undefined + } } }, { - ua_ch: '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"', + headers : { + 'sec-ch-ua': '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"' + }, expect: { - before: "HeadlessChrome", - after: "Chrome Headless" + browser : { + name : 'Chrome Headless', + version : '124', + major : '124', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Android WebView";v="123", "Not:A-Brand";v="8", "Chromium";v="123"' + }, + expect: { + browser : { + name : 'Chrome WebView', + version : '123', + major : '123', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"DuckDuckGo";v="131", "Chromium";v="131", "Not_A Brand";v="24"' + }, + expect : { + browser : { + name : 'DuckDuckGo', + version : '131', + major : '131', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"' + }, + expect: { + browser : { + name : 'Edge', + version : '120', + major : '120', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "HuaweiBrowser";v="114"' + }, + expect: { + browser : { + name : 'Huawei Browser', + version : '114', + major : '114', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Miui Browser";v="123", "Not:A-Brand";v="8", "Chromium";v="123"' + }, + expect: { + browser : { + name : 'MIUI Browser', + version : '123', + major : '123', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="130", "Oculus Browser";v="36", "Not?A_Brand";v="99"' + }, + expect: { + browser : { + name : 'Oculus Browser', + version : '36', + major : '36', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Opera";v="116", "Chromium";v="131", "Not_A Brand";v="24"' + }, + expect: { + browser : { + name : 'Opera', + version : '116', + major : '116', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Opera GX";v="114"' + }, + expect: { + browser : { + name : 'Opera GX', + version : '114', + major : '114', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"OperaMobile";v="86", ";Not A Brand";v="99", "Opera";v="115", "Chromium";v="130"' + }, + expect: { + browser : { + name : 'Opera Mobi', + version : '86', + major : '86', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="132", "OperaMobile";v="87", "Opera";v="117", " Not A;Brand";v="99"' + }, + expect: { + browser : { + name : 'Opera Mobi', + version : '87', + major : '87', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="125", "Not.A/Brand";v="24", "Samsung Internet";v="27.0"' + }, + expect: { + browser : { + name : 'Samsung Internet', + version : '27.0', + major : '27', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="130", "YaBrowser";v="24.12", "Not?A_Brand";v="99", "Yowser";v="2.5"' + }, + expect: { + browser : { + name : 'Yandex', + version : '24.12', + major : '24', + type : undefined + } } }, ] .forEach(test => { - it(`"${test.expect.before}" => "${test.expect.after}"`, () => { - const headers = { - 'sec-ch-ua' : test.ua_ch, - }; - const { browser } = UAParser(headers).withClientHints(); - assert.strictEqual(browser.name, test.expect.after); - }); + const { browser } = UAParser(test.headers).withClientHints(); + assert.deepEqual(browser, test.expect.browser); }); }); From 056e66f6a0caf79aedad55adb133eda49b0fd4eb Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 1 Mar 2025 06:50:16 +0700 Subject: [PATCH 329/388] Update contributors in package.json --- package.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/package.json b/package.json index 3ec26172c..00cbf2eee 100755 --- a/package.json +++ b/package.json @@ -25,14 +25,17 @@ "contributors": [ "Aamir Poonawalla ", "Admas ", + "Aiyush ", "algenon ", "Alvin Portillo ", "Amumu ", + "Andreas Kogler ", "Andrea Vaghi ", "Anton Zhiyanov ", "Arturo Mejia ", "Arun Rama Reddy ", "Austin Pray ", + "Beat YT <66485277+Beat-YT@users.noreply.github.com>", "Bendeguz ", "Benjamin Bertrand ", "Benjamin Urban ", @@ -44,6 +47,8 @@ "chenyuan-new <53860479+chenyuan-new@users.noreply.github.com>", "Christopher De Cairos ", "Cyrille David ", + "Dai Jie ", + "Danila Rodichkin ", "Dante ", "Dario Vladovic ", "David Annez ", @@ -73,11 +78,15 @@ "Grigory Dmitrenko ", "gulpin ", "Hans Ott ", + "Harald Reingruber <74898239+haraldreingruber-dedalus@users.noreply.github.com>", "Hendrik Helwich ", "Hermann Ebert ", "hr6r ", + "Hyewon Kang ", + "Hyunbin <47051820+hyunbinseo@users.noreply.github.com>", "Igor Topal ", "Ildar Kamalov ", + "Ilya Daraseliya ", "insanehong ", "jackpoll ", "Jacky Choo ", @@ -98,6 +107,7 @@ "Liam Quinn ", "Lithin ", "liujunlve ", + "lj0812 ", "ll-syber <670159357@qq.com>", "Loris Guignard ", "Lukas Drgon ", @@ -111,6 +121,7 @@ "Max Maurer ", "Max Nordlund ", "Michael Hess ", + "Mike ", "MimyyK ", "Mok ", "nabetama ", @@ -124,6 +135,7 @@ "o.drapeza ", "Oscar Becerra ", "otakuSiD ", + "Pablo Osés ", "Paris Morgan ", "patrick-nurt ", "Pavel Studeny ", From 178e678b15a9662956b64b9e998f328ede970e19 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 1 Mar 2025 08:08:44 +0700 Subject: [PATCH 330/388] [extensions] Update some fetcher bots #780 - `Google-PageRenderer`, `GoogleImageProxy`, `Snap URL Preview`, `SkypeUriPreview`, `TelegramBot` --- src/extensions/ua-parser-extensions.js | 9 ++-- test/data/ua/extension/fetcher.json | 60 ++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 1d528c9ee..46991cc59 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -222,22 +222,25 @@ const Fetchers = Object.freeze({ // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|telegram|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, + // Skype + /(skypeuripreview) preview\/([\w\.]+)/i, + // Slackbot - https://api.slack.com/robots /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, // WhatsApp - /(whatsapp)\/([\w\.]+)[\/ ][ianw]/i + /(whatsapp)\/([\w\.]+)/i ], [NAME, VERSION, [TYPE, FETCHER]], [ // Google Bots / Cohere / Snapchat / Vercelbot / Yandex Bots - /(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat|yandex(?:sitelinks|userproxy))/i + /(cohere-ai|vercelbot|feedfetcher-google|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|telegrambot|yandex(?:sitelinks|userproxy))/i ], [NAME, [TYPE, FETCHER]], ] diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 075281e87..41d819e6b 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -59,6 +59,16 @@ "type" : "fetcher" } }, + { + "desc" : "Google Image Proxy", + "ua" : "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)", + "expect" : + { + "name" : "GoogleImageProxy", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "Google Read Aloud - Mobile agent", "ua" : "Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36 (compatible; Google-Read-Aloud; +https://support.google.com/webmasters/answer/1061943)", @@ -79,6 +89,16 @@ "type" : "fetcher" } }, + { + "desc" : "Google Page Renderer", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Google-PageRenderer Google (+https://developers.google.com/+/web/snippet/) ", + "expect" : + { + "name" : "Google-PageRenderer", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "Google Publisher Center", "ua" : "GoogleProducer; (+https://developers.google.com/search/docs/crawling-indexing/google-producer)", @@ -129,6 +149,36 @@ "type" : "fetcher" } }, + { + "desc" : "Snap URL Preview", + "ua" : "Snap URL Preview Service; bot; snapchat; https://developers.snap.com/robots ", + "expect" : + { + "name" : "Snap URL Preview", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "SkypeUriPreview", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) SkypeUriPreview Preview/0.5 skype-url-preview@microsoft.com", + "expect" : + { + "name" : "SkypeUriPreview", + "version" : "0.5", + "type" : "fetcher" + } + }, + { + "desc" : "TelegramBot", + "ua" : "TelegramBot (like TwitterBot)", + "expect" : + { + "name" : "TelegramBot", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "UptimeRobot", "ua" : "Mozilla/5.0 (compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)", @@ -148,5 +198,15 @@ "version" : "undefined", "type" : "fetcher" } + }, + { + "desc" : "WhatsApp", + "ua" : "WhatsApp/2.23.20.0", + "expect" : + { + "name" : "WhatsApp", + "version" : "2.23.20.0", + "type" : "fetcher" + } } ] \ No newline at end of file From 3fccce26d06aae4e5c91a36dd15f1a838d75ce89 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 1 Mar 2025 09:18:18 +0700 Subject: [PATCH 331/388] GitHub Actions: test in various node versions --- .github/workflows/test-ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml index feb52062d..fee2ed4b1 100644 --- a/.github/workflows/test-ci.yml +++ b/.github/workflows/test-ci.yml @@ -11,12 +11,14 @@ jobs: strategy: matrix: arch: [amd64, ppc64le] + node-version: ['10.x', 'lts/*', 'node'] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 'lts/*' + node-version: ${{ matrix.node-version }} - name: Run the test run: | npm ci + npx playwright install --with-deps npm test From d1105ce4b35c072a6e9e447a380b4d1cb8eb2bf4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 1 Mar 2025 09:33:32 +0700 Subject: [PATCH 332/388] GitHub Actions: update node versions to be tested --- .github/workflows/test-ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml index fee2ed4b1..f1e262b8b 100644 --- a/.github/workflows/test-ci.yml +++ b/.github/workflows/test-ci.yml @@ -10,8 +10,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - arch: [amd64, ppc64le] - node-version: ['10.x', 'lts/*', 'node'] + arch: [amd64] + node-version: ['22.13', 'lts/*'] + include: + - arch: ppc64le + node-version: 20 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -19,6 +22,7 @@ jobs: node-version: ${{ matrix.node-version }} - name: Run the test run: | + echo "Running on ubuntu-latest-${{ matrix.arch }} with node version set as ${{ matrix.node-version }}" npm ci npx playwright install --with-deps npm test From 3d41647a55a71bc8755978b4a6a6c15302930f89 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 4 Mar 2025 11:14:31 +0700 Subject: [PATCH 333/388] [extensions] Update some more bots #780: `Better Uptime Bot`, `SemrushBot`, `Yahoo! Slurp` --- src/extensions/ua-parser-extensions.js | 8 +++---- test/data/ua/extension/crawler.json | 30 ++++++++++++++++++++++++++ test/data/ua/extension/fetcher.json | 10 +++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 46991cc59..09feadfa0 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -78,7 +78,7 @@ const Crawlers = Object.freeze({ /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, // SemrushBot - http://www.semrush.com/bot.html - /((?:semrush|splitsignal)bot[-abcfimostw]*)\/([\w\.-]+)/i, + /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, // Sogou Spider /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, @@ -110,7 +110,7 @@ const Crawlers = Object.freeze({ // Qihoo 360Spider // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|(?=yahoo! )slurp)/i + /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|yahoo! slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -219,7 +219,7 @@ const Fetchers = Object.freeze({ // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // DuckAssistBot - https://duckduckgo.com/duckassistbot/ - // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot + // Better Uptime / BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan // Yandex Bots - https://yandex.com/bots /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, @@ -240,7 +240,7 @@ const Fetchers = Object.freeze({ [ // Google Bots / Cohere / Snapchat / Vercelbot / Yandex Bots - /(cohere-ai|vercelbot|feedfetcher-google|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|telegrambot|yandex(?:sitelinks|userproxy))/i + /((?:better uptime |telegram|vercel)bot|cohere-ai|feedfetcher-google|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|yandex(?:sitelinks|userproxy))/i ], [NAME, [TYPE, FETCHER]], ] diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 48c586508..2a4d09637 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -480,6 +480,26 @@ "type" : "crawler" } }, + { + "desc" : "SemrushBot for Backlink Audit tool", + "ua" : "Mozilla/5.0 (compatible; SemrushBot-BA; +http://www.semrush.com/bot.html)", + "expect" : + { + "name" : "SemrushBot-BA", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "SemrushBot for On Page SEO Checker tool", + "ua" : "Mozilla/5.0 (compatible; SemrushBot-SI/0.97; +http://www.semrush.com/bot.html)", + "expect" : + { + "name" : "SemrushBot-SI", + "version" : "0.97", + "type" : "crawler" + } + }, { "desc" : "SemrushBot for ContentShake AI tool", "ua" : "Mozilla/5.0 (compatible; SemrushBot-OCOB/1; +https://www.semrush.com/bot/)", @@ -550,6 +570,16 @@ "type" : "crawler" } }, + { + "desc" : "Yahoo! Slurp", + "ua" : "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp) ", + "expect" : + { + "name" : "Yahoo! Slurp", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "YandexBot", "ua" : "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 41d819e6b..b3854284e 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -9,6 +9,16 @@ "type" : "fetcher" } }, + { + "desc" : "Better Uptime Bot", + "ua" : "Better Uptime Bot Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36", + "expect" : + { + "name" : "Better Uptime Bot", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "BingPreview", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b", From baeac63ebeda9830834292ba2e013738bad78f1c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Mar 2025 19:17:12 +0700 Subject: [PATCH 334/388] Remover Jazzer.js fuzz testing, since the package is no longer supported & leads to failing build --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 00cbf2eee..d6a3dc1d8 100755 --- a/package.json +++ b/package.json @@ -233,7 +233,6 @@ "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.23.2", - "@jazzer.js/core": "^1.4.0", "@playwright/test": "^1.49.0", "jshint": "~2.13.6", "mocha": "~8.2.0", From 8a05328ce09dcda4d5d0cadec77e3aa638cba7ab Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 6 Mar 2025 21:03:29 +0700 Subject: [PATCH 335/388] [extensions] Improve inapp browser detection: Slack, Yahoo! Japan --- src/extensions/ua-parser-extensions.js | 6 +++-- test/data/ua/extension/inapp.json | 32 ++++++++++++++++++++++++++ test/unit/extensions.js | 4 +++- 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 test/data/ua/extension/inapp.json diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 09feadfa0..f1da2ac3a 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -253,10 +253,12 @@ const Fetchers = Object.freeze({ const InApps = Object.freeze({ browser : [ // Slack - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]], + [/(?:slack(?=.+electron|.+ios)|chatlyio)\/([\d\.]+)/i], + [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], // Yahoo! Japan - [/jp\.co\.yahoo\.android\.yjtop\/([\d\.]+)/i], [VERSION, 'Yahoo! Japan', [TYPE, INAPP]] + [/jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], + [VERSION, [NAME, 'Yahoo! Japan'], [TYPE, INAPP]] ] }); diff --git a/test/data/ua/extension/inapp.json b/test/data/ua/extension/inapp.json new file mode 100644 index 000000000..55124651a --- /dev/null +++ b/test/data/ua/extension/inapp.json @@ -0,0 +1,32 @@ +[ + { + "desc" : "Slack on mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Slack/4.39.90 Chrome/127.0.6533.72 Electron/13.1.9 Safari/537.36", + "expect" : + { + "name" : "Slack", + "version" : "4.39.90", + "type" : "inapp" + } + }, + { + "desc" : "Yahoo! Japan on Android", + "ua" : "Mozilla/5.0 (Linux; Android 13; SH-M20 Build/TKQ1.220915.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36 YJApp-ANDROID jp.co.yahoo.android.yjtop/3.187.0", + "expect" : + { + "name" : "Yahoo! Japan", + "version" : "3.187.0", + "type" : "inapp" + } + }, + { + "desc" : "Yahoo! Japan on iOS", + "ua" : "Mozilla/5.0 (iPad; CPU OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 YJApp-IOS jp.co.yahoo.ipn.appli/4.131.0", + "expect" : + { + "name" : "Yahoo! Japan", + "version" : "4.131.0", + "type" : "inapp" + } + } +] diff --git a/test/unit/extensions.js b/test/unit/extensions.js index 0f04732ee..f4548f3da 100644 --- a/test/unit/extensions.js +++ b/test/unit/extensions.js @@ -8,8 +8,9 @@ const clis = require('../data/ua/extension/cli.json'); const crawlers = require('../data/ua/extension/crawler.json'); const emails = require('../data/ua/extension/email.json'); const fetchers = require('../data/ua/extension/fetcher.json'); +const inapps = require('../data/ua/extension/inapp.json'); const libraries = require('../data/ua/extension/library.json'); -const { Bots, CLIs, Crawlers, Emails, Fetchers, Libraries } = require('../../src/extensions/ua-parser-extensions'); +const { Bots, CLIs, Crawlers, Emails, Fetchers, InApps, Libraries } = require('../../src/extensions/ua-parser-extensions'); describe('Extensions', () => { [ @@ -17,6 +18,7 @@ describe('Extensions', () => { ['Crawlers', crawlers, Crawlers], ['Emails', emails, Emails], ['Fetchers', fetchers, Fetchers], + ['InApps', inapps, InApps], ['Libraries', libraries, Libraries] ] .forEach((list) => { From a9b31c0f7b7e092d9afadfaf381809e6201c8d0c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 7 Mar 2025 00:13:37 +0700 Subject: [PATCH 336/388] [extensions] Add bots: AdIdxBot, Linespider, LinkedInBot, MicrosoftPreview, OpenAI Image Downloader --- src/extensions/ua-parser-extensions.js | 17 +-- test/data/ua/extension/crawler.json | 186 +++++++++++++++++++++++-- test/data/ua/extension/fetcher.json | 20 +++ 3 files changed, 202 insertions(+), 21 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index f1da2ac3a..761b3c884 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -42,24 +42,25 @@ const Crawlers = Object.freeze({ [ // AhrefsBot - https://ahrefs.com/robot // Amazonbot - https://developer.amazon.com/amazonbot - // Bingbot - http://www.bing.com/bingbot.htm + // Bingbot / AdIdxBot - https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0 // CCBot - https://commoncrawl.org/faq // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot + // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot - /(applebot(?:-extended)?)\/([\w\.]+)/i, + /(applebot(?:-extended)?)\/?([\w\.]*)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 - /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, + /(baiduspider[-imagevdonwsfcpr]{0,7})\/?([\w\.]*)/i, // ClaudeBot (Anthropic) /(claude(?:bot|-web)|anthropic-ai)\/?([\w\.]*)/i, @@ -92,8 +93,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |line|yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -219,10 +220,10 @@ const Fetchers = Object.freeze({ // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // DuckAssistBot - https://duckduckgo.com/duckassistbot/ - // Better Uptime / BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot + // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 2a4d09637..8ed2d0099 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -9,6 +9,16 @@ "type" : "crawler" } }, + { + "desc" : "AdIdxBot", + "ua" : "Mozilla/5.0 (compatible; adidxbot/2.0; +http://www.bing.com/bingbot.htm)", + "expect" : + { + "name" : "adidxbot", + "version" : "2.0", + "type" : "crawler" + } + }, { "desc" : "AdsBot Mobile Web", "ua" : "AdsBot-Google (+http://www.google.com/adsbot.html)", @@ -79,6 +89,16 @@ "type" : "crawler" } }, + { + "desc" : "Applebot-Extended", + "ua" : "Applebot-Extended", + "expect" : + { + "name" : "Applebot-Extended", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Amazonbot", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)", @@ -89,6 +109,126 @@ "type" : "crawler" } }, + { + "desc" : "Anthropic AI", + "ua" : "anthropic-ai", + "expect" : + { + "name" : "anthropic-ai", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Archive.org Bot", + "ua" : "ia_archiver/8.1 (Windows 2000 1.9; en-US;)", + "expect" : + { + "name" : "ia_archiver", + "version" : "8.1", + "type" : "crawler" + } + }, + { + "desc" : "Archive.org Bot", + "ua" : "Mozilla/5.0 (compatible; archive.org_bot/3.3.0 +https://archive.org/details/archive.org_bot)", + "expect" : + { + "name" : "archive.org_bot", + "version" : "3.3.0", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider", + "ua" : "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", + "expect" : + { + "name" : "Baiduspider", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-ads", + "ua" : "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0; Baiduspider-ads) Gecko/17.0 Firefox/17.0", + "expect" : + { + "name" : "Baiduspider-ads", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-cpro", + "ua" : "Mozilla/5.0 (compatible; Baiduspider-cpro; +http://www.baidu.com/search/spider.html)", + "expect" : + { + "name" : "Baiduspider-cpro", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-favo", + "ua" : "Baiduspider-favo", + "expect" : + { + "name" : "Baiduspider-favo", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-image", + "ua" : "Baiduspider-image+(+http://www.baidu.com/search/spider.htm)", + "expect" : + { + "name" : "Baiduspider-image", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-news", + "ua" : "Baiduspider-news", + "expect" : + { + "name" : "Baiduspider-news", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-render", + "ua" : "Mozilla/5.0 (compatible; Baiduspider-render/2.0; +http://www.baidu.com/search/spider.html)", + "expect" : + { + "name" : "Baiduspider-render", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-video", + "ua" : "Baiduspider-video", + "expect" : + { + "name" : "Baiduspider-video", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Bingbot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/", + "expect" : + { + "name" : "bingbot", + "version" : "2.0", + "type" : "crawler" + } + }, { "desc" : "Bytespider", "ua" : "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider", @@ -179,6 +319,16 @@ "type" : "crawler" } }, + { + "desc" : "DuckDuckBot", + "ua" : "DuckDuckBot/1.1; ( http://duckduckgo.com/duckduckbot.html)", + "expect" : + { + "name" : "DuckDuckBot", + "version" : "1.1", + "type" : "crawler" + } + }, { "desc" : "Exabot", "ua" : "Mozilla/5.0 (compatible; Exabot/3.0; +http://www.exabot.com/go/robot)", @@ -340,32 +490,32 @@ } }, { - "desc" : "Archive.org Bot", - "ua" : "ia_archiver/8.1 (Windows 2000 1.9; en-US;)", + "desc" : "ImagesiftBot", + "ua" : "Mozilla/5.0 (compatible; ImagesiftBot; +imagesift.com)", "expect" : { - "name" : "ia_archiver", - "version" : "8.1", + "name" : "ImagesiftBot", + "version" : "undefined", "type" : "crawler" } }, { - "desc" : "Archive.org Bot", - "ua" : "Mozilla/5.0 (compatible; archive.org_bot/3.3.0 +https://archive.org/details/archive.org_bot)", + "desc" : "Linespider", + "ua" : "Mozilla/5.0 (compatible; Linespider/1.1; +https://lin.ee/4dwXkTH)", "expect" : { - "name" : "archive.org_bot", - "version" : "3.3.0", + "name" : "Linespider", + "version" : "1.1", "type" : "crawler" } }, { - "desc" : "ImagesiftBot", - "ua" : "Mozilla/5.0 (compatible; ImagesiftBot; +imagesift.com)", + "desc" : "LinkedInBot", + "ua" : "LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com)", "expect" : { - "name" : "ImagesiftBot", - "version" : "undefined", + "name" : "LinkedInBot", + "version" : "1.0", "type" : "crawler" } }, @@ -462,7 +612,7 @@ }, { "desc" : "PetalBot", - "ua" : "Mozilla/5.0 (Linux; Android 7.0;) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; PetalBot;+https://webmaster.petalsearch.com/site/petalbot) ", + "ua" : "Mozilla/5.0 (Linux; Android 7.0;) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; PetalBot;+https://webmaster.petalsearch.com/site/petalbot)", "expect" : { "name" : "PetalBot", @@ -520,6 +670,16 @@ "type" : "crawler" } }, + { + "desc" : "Sogou", + "ua" : "Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)", + "expect" : + { + "name" : "Sogou web spider", + "version" : "4.0", + "type" : "crawler" + } + }, { "desc" : "Teoma", "ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index b3854284e..fed25bc4f 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -139,6 +139,26 @@ "type" : "fetcher" } }, + { + "desc" : "MicrosoftPreview", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; MicrosoftPreview/2.0; +https://aka.ms/MicrosoftPreview) Chrome/W.X.Y.Z Safari/537.36", + "expect" : + { + "name" : "MicrosoftPreview", + "version" : "2.0", + "type" : "fetcher" + } + }, + { + "desc" : "Pinterestbot", + "ua" : "Mozilla/5.0 (compatible; Pinterestbot/1.0; +http://www.pinterest.com/bot.html)", + "expect" : + { + "name" : "Pinterestbot", + "version" : "1.0", + "type" : "fetcher" + } + }, { "desc" : "Rogerbot", "ua" : "Mozilla/5.0 (compatible; rogerBot/1.0; UrlCrawler; http://www.seomoz.org/dp/rogerbot)", From 367eae4c85e7b5bd70b26a73a49e79edb95db314 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 7 Mar 2025 10:14:37 +0700 Subject: [PATCH 337/388] [extensions] Add new Vehicles: `BMW`, `Jeep Wagooner` --- src/extensions/ua-parser-extensions.js | 19 +++++--- test/data/ua/extension/vehicle.json | 62 ++++++++++++++++++++++++++ test/unit/extensions.js | 44 +++++++++--------- 3 files changed, 97 insertions(+), 28 deletions(-) create mode 100644 test/data/ua/extension/vehicle.json diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 761b3c884..7c24624a4 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -335,15 +335,20 @@ const Libraries = Object.freeze({ const Vehicles = Object.freeze({ device : [ - [ - /dilink.+(byd) auto/i, // BYD - ], [VENDOR], [ + [/aftlbt962e2/i], // BMW + [[VENDOR, 'BMW']], + + [/dilink.+(byd) auto/i], // BYD + [VENDOR], - /(rivian) (r1t)/i, // Rivian - ], [VENDOR, MODEL], [ + [/aftlft962x3/i], // Jeep + [[VENDOR, 'Jeep'], [MODEL, 'Wagooner']], + + [/(rivian) (r1t)/i], // Rivian + [VENDOR, MODEL], - /vcc.+netfront/i, // Volvo - ], [[VENDOR, 'Volvo']] + [/vcc.+netfront/i], // Volvo + [[VENDOR, 'Volvo']] ] }); diff --git a/test/data/ua/extension/vehicle.json b/test/data/ua/extension/vehicle.json new file mode 100644 index 000000000..780005110 --- /dev/null +++ b/test/data/ua/extension/vehicle.json @@ -0,0 +1,62 @@ +[ + { + "desc" : "BMW", + "ua" : "Mozilla/5.0 (Linux; Android 9; AFTLBT962E2) AppleWebKit/537.36 (KHTML, like Gecko) Silk/118.3.1 like Chrome/118.0.5993.155 Safari/537.36", + "expect" : + { + "vendor" : "BMW", + "model" : "undefined", + "type" : "undefined" + } + }, + { + "desc" : "BYD", + "ua" : "Mozilla/5.0 (Linux; Android 10; DiLink3.0 For BYD AUTO Build/QKQ1.200816.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.186 Safari/537.36", + "expect" : + { + "vendor" : "BYD", + "model" : "undefined", + "type" : "undefined" + } + }, + { + "desc" : "Jeep", + "ua" : "Mozilla/5.0 (Linux; Android 9; AFTLFT962X3) AppleWebKit/537.36 (KHTML, like Gecko) Silk/124.5.2 like Chrome/124.0.6367.248 Safari/537.36", + "expect" : + { + "vendor" : "Jeep", + "model" : "Wagooner", + "type" : "undefined" + } + }, + { + "desc" : "Rivian", + "ua" : "Dalvik/2.1.0 (Linux; U; Android 10; Rivian R1T Build/QQ3A.200605.002)", + "expect" : + { + "vendor" : "Rivian", + "model" : "R1T", + "type" : "undefined" + } + }, + { + "desc" : "Tesla", + "ua" : "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.36.16-3e9e4e8dd287", + "expect" : + { + "vendor" : "Tesla", + "model" : "undefined", + "type" : "embedded" + } + }, + { + "desc" : "Volvo", + "ua" : "Mozilla/5.0 (VCC; 1.0; like Gecko) NetFront/4.2", + "expect" : + { + "vendor" : "Volvo", + "model" : "undefined", + "type" : "undefined" + } + } +] diff --git a/test/unit/extensions.js b/test/unit/extensions.js index f4548f3da..417ed8a59 100644 --- a/test/unit/extensions.js +++ b/test/unit/extensions.js @@ -4,31 +4,33 @@ const parseJS = require('@babel/parser').parse; const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); const { UAParser } = require('../../src/main/ua-parser'); -const clis = require('../data/ua/extension/cli.json'); -const crawlers = require('../data/ua/extension/crawler.json'); -const emails = require('../data/ua/extension/email.json'); -const fetchers = require('../data/ua/extension/fetcher.json'); -const inapps = require('../data/ua/extension/inapp.json'); -const libraries = require('../data/ua/extension/library.json'); -const { Bots, CLIs, Crawlers, Emails, Fetchers, InApps, Libraries } = require('../../src/extensions/ua-parser-extensions'); +const { Bots, CLIs, Crawlers, Emails, Fetchers, InApps, Libraries, Vehicles } = require('../../src/extensions/ua-parser-extensions'); describe('Extensions', () => { [ - ['CLIs', clis, CLIs], - ['Crawlers', crawlers, Crawlers], - ['Emails', emails, Emails], - ['Fetchers', fetchers, Fetchers], - ['InApps', inapps, InApps], - ['Libraries', libraries, Libraries] + ['CLIs', 'cli', CLIs], + ['Crawlers', 'crawler', Crawlers], + ['Emails', 'email', Emails], + ['Fetchers', 'fetcher', Fetchers], + ['InApps', 'inapp', InApps], + ['Libraries', 'library', Libraries], + ['Vehicles', 'vehicle', Vehicles] ] - .forEach((list) => { - describe(list[0], () => { - list[1].forEach((agent) => { - it(`Can detect ${agent.desc}: "${agent.ua}"`, () => { - let browser = UAParser(agent.ua, list[2]).browser; - assert.strictEqual(String(browser.name), agent.expect.name); - assert.strictEqual(String(browser.version), agent.expect.version); - assert.strictEqual(String(browser.type), agent.expect.type); + .forEach(([desc, path, ext]) => { + const tests = require(`../data/ua/extension/${path}.json`); + describe(desc, () => { + tests.forEach((test) => { + it(`Can detect ${test.desc}: "${test.ua}"`, () => { + const { browser, device } = UAParser(test.ua, ext); + if ('browser' in ext) { + assert.strictEqual(String(browser.name), test.expect.name); + assert.strictEqual(String(browser.version), test.expect.version); + assert.strictEqual(String(browser.type), test.expect.type); + } else if ('device' in ext) { + assert.strictEqual(String(device.vendor), test.expect.vendor); + assert.strictEqual(String(device.model), test.expect.model); + assert.strictEqual(String(device.type), test.expect.type); + } }); }); }); From 0cf44773b144b120fb72be5d565658225962e344 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Mar 2025 11:37:54 +0700 Subject: [PATCH 338/388] Improve device detection: Archos --- src/main/ua-parser.js | 9 +- test/data/ua/device/archos.json | 227 ++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 test/data/ua/device/archos.json diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 31c1c3302..85cbec508 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -713,6 +713,14 @@ /droid.+; (a(?:015|06[35]|142p?))/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ + // Archos + /; (x67 5g|tikeasy \w+|ac[1789]\d\w+)( b|\))/i, + /archos ?(5|gamepad2?|([\w ]*[t1789]|hello) ?\d+[\w ]*)( b|\))/i + ], [MODEL, [VENDOR, 'Archos'], [TYPE, TABLET]], [ + /archos ([\w ]+)( b|\))/i, + /; (ac[3-6]\d\w{2,8})( b|\))/i + ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [ + // MIXED /(imo) (tab \w+)/i, // IMO /(infinix) (x1101b?)/i // Infinix XPad @@ -728,7 +736,6 @@ ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /(kobo)\s(ereader|touch)/i, // Kobo - /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad /(kindle)\/([\w\.]+)/i // Kindle ], [VENDOR, MODEL, [TYPE, TABLET]], [ diff --git a/test/data/ua/device/archos.json b/test/data/ua/device/archos.json new file mode 100644 index 000000000..53628c535 --- /dev/null +++ b/test/data/ua/device/archos.json @@ -0,0 +1,227 @@ +[ + { + "desc": "Archos 5", + "ua": "Mozilla/5.0 (Linux; U; Android 1.6; fr-fr; Archos5 Build/Donut) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1", + "expect": { + "vendor": "Archos", + "model": "5", + "type": "tablet" + } + }, + { + "desc": "Archos 40b Titanium Surround", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Archos 40b Titanium Surround Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "40b Titanium Surround", + "type": "mobile" + } + }, + { + "desc": "Archos 40c Titanium v2", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ARCHOS 40C TIv2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "40C TIv2", + "type": "mobile" + } + }, + { + "desc": "Archos 45 Neon", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Archos 45 Neon Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "45 Neon", + "type": "mobile" + } + }, + { + "desc": "Archos 45 Neon", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; AC45NE Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 YaBrowser/15.6.2311.6088.00 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC45NE", + "type": "mobile" + } + }, + { + "desc": "Archos 45B Helium", + "ua": "Mozilla/5.0 (Linux; Android 7.0; AC45BHE Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC45BHE", + "type": "mobile" + } + }, + { + "desc": "Archos 45B Titanium", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Archos 45B Titanium) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "45B Titanium", + "type": "mobile" + } + }, + { + "desc": "Archos 50 Cesium", + "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; ARCHOS; AC50CE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", + "expect": { + "vendor": "Archos", + "model": "AC50CE", + "type": "mobile" + } + }, + { + "desc": "Archos 50B Helium 4G", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; AC50BHE Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC50BHE", + "type": "mobile" + } + }, + { + "desc": "Archos 55 diamond Selfie", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Archos 55 diamond Selfie Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.91 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "55 diamond Selfie", + "type": "mobile" + } + }, + { + "desc": "Archos 80 G9", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; zh-tw; ARCHOS 80G9 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "Archos", + "model": "80G9", + "type": "tablet" + } + }, + { + "desc": "Archos 80 Xenon", + "ua": "Mozilla/5.0 (Linux; Android 4.1.2; Archos 80 Xenon Build/JZO54K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Safari/537.36 OPR/50.6.2426.201126", + "expect": { + "vendor": "Archos", + "model": "80 Xenon", + "type": "tablet" + } + }, + { + "desc": "Archos 97c Platinum", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Archos 97c Platinum Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "97c Platinum", + "type": "tablet" + } + }, + { + "desc": "Archos 101 Access 3G V2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Archos Access 101 3G V2 Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.130 Safari/537.36[FBAN/EMA;FBLC/pt_PT;FBAV/360.0.0.7.53;]", + "expect": { + "vendor": "Archos", + "model": "Access 101 3G V2", + "type": "tablet" + } + }, + { + "desc": "Archos 101 Oxygen 4G", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Archos 101 Oxygen 4G Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.71 Safari/537.36 [FB_IAB/FB4A;FBAV/374.0.0.20.109;]", + "expect": { + "vendor": "Archos", + "model": "101 Oxygen 4G", + "type": "tablet" + } + }, + { + "desc": "Archos 101 Platinum 3G V2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; AC101PL3GV2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC101PL3GV2", + "type": "tablet" + } + }, + { + "desc": "Archos 101B Helium 4G", + "ua": "Mozilla/5.0 (Linux; Android 6.0; AC101BHE Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC101BHE", + "type": "tablet" + } + }, + { + "desc": "Archos 101s Oxygen Ardoiz", + "ua": "Mozilla/5.0 (Linux; Android 9; Archos Oxygen 101S ARDOIZ Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.102 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "Oxygen 101S ARDOIZ", + "type": "tablet" + } + }, + { + "desc": "Archos GAMEPAD2", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; ARCHOS GAMEPAD2 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.94 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "GAMEPAD2", + "type": "tablet" + } + }, + { + "desc": "Archos Hello 7", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Archos Hello 7 Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.53 Safari/537.36 GoogleApp/13.24.9.26.arm64", + "expect": { + "vendor": "Archos", + "model": "Hello 7", + "type": "tablet" + } + }, + { + "desc": "Archos Sense 101 X", + "ua": "Mozilla/5.0 (Linux; arm; Android 7.0; Archos Sense 101 X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 YaBrowser/20.2.0.215.01 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "Sense 101 X", + "type": "tablet" + } + }, + { + "desc": "Archos T101 FHD WiFi", + "ua": "Mozilla/5.0 (Linux; Android 13; ARCHOS T101 FHD WiFi Build/T00624; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.159 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "T101 FHD WiFi", + "type": "tablet" + } + }, + { + "desc": "Archos Tikeasy 10d", + "ua": "Mozilla/5.0 (Linux; Android 13; Tikeasy 10d Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.260 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "Tikeasy 10d", + "type": "tablet" + } + }, + { + "desc": "Archos T96 WIFI", + "ua": "Mozilla/5.0 (Linux; Android 11; ARCHOS T96 WIFI_EEA Build/RP1A.201005.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.5005.78 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "T96 WIFI_EEA", + "type": "tablet" + } + }, + { + "desc": "Archos X67 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; X67 5G Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.58 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "X67 5G", + "type": "tablet" + } + } +] \ No newline at end of file From 30c0b20c207405359a0fabe0ab9b78df78dae298 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Mar 2025 12:42:20 +0700 Subject: [PATCH 339/388] Improve device detection: LG --- src/main/ua-parser.js | 8 +++++--- test/data/ua/device/lg.json | 9 +++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 85cbec508..f2a15366f 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -606,7 +606,7 @@ /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, - /\blg[-e;\/ ]+((?!browser|netcast|android tv|watch)\w+)/i, + /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch))(\w+)/i, /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ @@ -763,7 +763,9 @@ ], [VENDOR, [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ - /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV + /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ + /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i ], [[VENDOR, LG], [TYPE, SMARTTV]], [ /(apple) ?tv/i // Apple TV ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ @@ -938,7 +940,7 @@ /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen - /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/; ]?([\d\.]*)/i + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i ], [NAME, VERSION], [ /\(bb(10);/i // BlackBerry 10 ], [VERSION, [NAME, BLACKBERRY]], [ diff --git a/test/data/ua/device/lg.json b/test/data/ua/device/lg.json index b47e99f00..9d63ac146 100644 --- a/test/data/ua/device/lg.json +++ b/test/data/ua/device/lg.json @@ -134,6 +134,15 @@ "type": "smarttv" } }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen Safari/537.41 LG Browser/7.00.00(LGE; 42LB670V-ZA; 05.05.90; 1); webOS.TV-2014; LG NetCast.TV-2013 Compatible (LGE, 42LB670V-ZA, wireless)", + "expect": { + "vendor": "LG", + "model": "42LB670V-ZA", + "type": "smarttv" + } + }, { "desc": "LG Android TV", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; LG Android TV Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", From 2d26eada9adf31c2af34b85e98c105dbbfc5de6c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Mar 2025 12:49:29 +0700 Subject: [PATCH 340/388] Add new browser: LG Browser --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 4 ++-- test/data/ua/browser/browser-all.json | 10 +++++----- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index c279436cf..5f7a73464 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -73,6 +73,7 @@ export const Browser: Readonly<{ KINDLE: "Kindle"; LENOVO: "Smart Lenovo Browser"; LADYBIRD: "Ladybird"; + LG: "LG Browser"; LIBREWOLF: "LibreWolf"; LIEBAO: "LBBROWSER"; LINE: "Line"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 25b8de520..d5cdc426f 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -78,6 +78,7 @@ const Browser = Object.freeze({ KINDLE: 'Kindle', LENOVO: 'Smart Lenovo Browser', LADYBIRD: 'Ladybird', + LG: 'LG Browser', LIBREWOLF: 'LibreWolf', LIEBAO: 'LBBROWSER', LINE: 'Line', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index f2a15366f..1b22b7e86 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -353,8 +353,8 @@ /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer - // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon)\/([-\w\.]+)/i, + // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|lg browser)\/([-\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon /(heytap|ovi|115)browser\/([\d\.]+)/i, // HeyTap/Ovi/115 /(weibo)__([\d\.]+)/i // Weibo diff --git a/test/data/ua/browser/browser-all.json b/test/data/ua/browser/browser-all.json index 05dfd4fce..9d0344b8c 100644 --- a/test/data/ua/browser/browser-all.json +++ b/test/data/ua/browser/browser-all.json @@ -1067,13 +1067,13 @@ } }, { - "desc" : "LibreWolf", - "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:91.0) Gecko/20100101 LibreWolf/91.0", + "desc" : "LG Browser", + "ua" : "Mozilla/5.0 (Unknown; Linux armv7l) AppleWebKit/537.1+ (KHTML, like Gecko) Safari/537.1+ LG Browser/6.00.00(+mouse+3D+SCREEN+TUNER; LGE; 47LA621V-ZD; 04.28.17; 0x00000001;); LG NetCast.TV-2013 /04.28.17 (LG, 47LA621V-ZD, wired)", "expect" : { - "name" : "LibreWolf", - "version" : "91.0", - "major" : "91" + "name" : "LG Browser", + "version" : "6.00.00", + "major" : "6" } }, { From 8f545f9e3fd99ca2890bc1f7d6b92260eca35c0f Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 8 Mar 2025 16:18:31 +0700 Subject: [PATCH 341/388] Add new browser: Dooble, Ecosia, Otter, qutebrowser, Surf --- src/enums/ua-parser-enums.d.ts | 5 +++ src/enums/ua-parser-enums.js | 5 +++ src/main/ua-parser.js | 8 ++-- test/data/ua/browser/browser-all.json | 60 +++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 5f7a73464..9ea1ffec8 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -32,9 +32,11 @@ export const Browser: Readonly<{ DAUM: "Daum"; DILLO: "Dillo"; DOLPHIN: "Dolphin"; + DOOBLE: 'Dooble', DORIS: "Doris"; DRAGON: "Dragon"; DUCKDUCKGO: "DuckDuckGo"; + ECOSIA: "Ecosia"; EDGE: "Edge"; EPIPHANY: "Epiphany"; FACEBOOK: "Facebook"; @@ -103,6 +105,7 @@ export const Browser: Readonly<{ OPERA_MOBI: "Opera Mobi"; OPERA_TABLET: "Opera Tablet"; OPERA_TOUCH: "Opera Touch"; + OTTER: "Otter"; OVI: "OviBrowser"; PALEMOON: "PaleMoon"; PHANTOMJS: "PhantomJS"; @@ -114,6 +117,7 @@ export const Browser: Readonly<{ QQ_LITE: "QQBrowserLite"; QUARK: "Quark"; QUPZILLA: "QupZilla"; + QUTEBROWSER: "qutebrowser"; REKONQ: "rekonq"; ROCKMELT: "Rockmelt"; SAFARI: "Safari"; @@ -130,6 +134,7 @@ export const Browser: Readonly<{ SNAPCHAT: "Snapchat"; SOGOU_EXPLORER: "Sogou Explorer"; SOGOU_MOBILE: "Sogou Mobile"; + SURF: "Surf"; SWIFTFOX: "Swiftfox"; TESLA: "Tesla"; TIKTOK: "TikTok"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index d5cdc426f..090035792 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -37,9 +37,11 @@ const Browser = Object.freeze({ DAUM: 'Daum', DILLO: 'Dillo', DOLPHIN: 'Dolphin', + DOOBLE: 'Dooble', DORIS: 'Doris', DRAGON: 'Dragon', DUCKDUCKGO: 'DuckDuckGo', + ECOSIA: 'Ecosia', EDGE: 'Edge', EPIPHANY: 'Epiphany', FACEBOOK: 'Facebook', @@ -108,6 +110,7 @@ const Browser = Object.freeze({ OPERA_MOBI: 'Opera Mobi', OPERA_TABLET: 'Opera Tablet', OPERA_TOUCH: 'Opera Touch', + OTTER: 'Otter', OVI: 'OviBrowser', PALEMOON: 'PaleMoon', PHANTOMJS: 'PhantomJS', @@ -119,6 +122,7 @@ const Browser = Object.freeze({ QQ_LITE: 'QQBrowserLite', QUARK: 'Quark', QUPZILLA: 'QupZilla', + QUTEBROWSER: 'qutebrowser', REKONQ: 'rekonq', ROCKMELT: 'Rockmelt', SAFARI: 'Safari', @@ -135,6 +139,7 @@ const Browser = Object.freeze({ SNAPCHAT: 'Snapchat', SOGOU_EXPLORER: 'Sogou Explorer', SOGOU_MOBILE: 'Sogou Mobile', + SURF: 'Surf', SWIFTFOX: 'Swiftfox', TESLA: 'Tesla', TIKTOK: 'TikTok', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 1b22b7e86..4966699f0 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -353,11 +353,11 @@ /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer - // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|lg browser)\/([-\w\.]+)/i, + // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser)\/([-\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon - /(heytap|ovi|115)browser\/([\d\.]+)/i, // HeyTap/Ovi/115 - /(weibo)__([\d\.]+)/i // Weibo + /(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf + /(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo ], [NAME, VERSION], [ /quark(?:pc)?\/([-\w\.]+)/i // Quark ], [VERSION, [NAME, 'Quark']], [ diff --git a/test/data/ua/browser/browser-all.json b/test/data/ua/browser/browser-all.json index 9d0344b8c..3fc0e5e57 100644 --- a/test/data/ua/browser/browser-all.json +++ b/test/data/ua/browser/browser-all.json @@ -511,6 +511,16 @@ "major" : "2" } }, + { + "desc" : "Dooble", + "ua" : "Mozilla/5.0 (X11; Haiku BePC) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.17 Chrome/87.0.4280.144 Safari/537.36 Dooble/2023.12.25 Dooble/2023.12.25", + "expect" : + { + "name" : "Dooble", + "version" : "2023.12.25", + "major" : "2023" + } + }, { "desc" : "Doris", "ua" : "Doris/1.15 [en] (Symbian)", @@ -541,6 +551,26 @@ "major" : "5" } }, + { + "desc" : "Ecosia on Android", + "ua" : "Mozilla/5.0 (Linux; Android 10; SM-G975U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36 (Ecosia android@85.0.4183.127)", + "expect" : + { + "name" : "Ecosia", + "version" : "85.0.4183.127", + "major" : "85" + } + }, + { + "desc" : "Ecosia on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Mobile/14G60 (Ecosia ios@3.0.1.533)", + "expect" : + { + "name" : "Ecosia", + "version" : "3.0.1.533", + "major" : "3" + } + }, { "desc" : "Epiphany", "ua" : "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7) Gecko/20040628 Epiphany/1.2.6", @@ -1006,6 +1036,16 @@ "major" : "5" } }, + { + "desc" : "Otter", + "ua" : "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/602.1 (KHTML, like Gecko) Otter/1.0.81", + "expect" : + { + "name" : "Otter", + "version" : "1.0.81", + "major" : "1" + } + }, { "desc" : "PicoBrowser", "ua" : "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36", @@ -1066,6 +1106,16 @@ "major" : "9" } }, + { + "desc" : "Surf Browser", + "ua" : "Mozilla/5.0 (Android 6.0; HUAWEI ALE-L21) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36 SurfBrowser/3.0", + "expect" : + { + "name" : "Surf", + "version" : "3.0", + "major" : "3" + } + }, { "desc" : "LG Browser", "ua" : "Mozilla/5.0 (Unknown; Linux armv7l) AppleWebKit/537.1+ (KHTML, like Gecko) Safari/537.1+ LG Browser/6.00.00(+mouse+3D+SCREEN+TUNER; LGE; 47LA621V-ZD; 04.28.17; 0x00000001;); LG NetCast.TV-2013 /04.28.17 (LG, 47LA621V-ZD, wired)", @@ -1628,6 +1678,16 @@ "major" : "1" } }, + { + "desc" : "qutebrowser", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) qutebrowser/2.4.0 QtWebEngine/5.15.6 Chrome/95.0.4628.2 Safari/537.36", + "expect" : + { + "name" : "qutebrowser", + "version" : "2.4.0", + "major" : "2" + } + }, { "desc" : "Rekonq 2", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) rekonq/2.2.1 Safari/537.21", From f7f64a31eae0ca20d9cfa28bd2732e1a110ca16a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Mar 2025 17:50:36 +0700 Subject: [PATCH 342/388] [extensions] Detect OS from WhatsApp user agent --- src/extensions/ua-parser-extensions.js | 8 ++++++++ test/unit/extensions.js | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 7c24624a4..e8e3f6e80 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -244,6 +244,11 @@ const Fetchers = Object.freeze({ /((?:better uptime |telegram|vercel)bot|cohere-ai|feedfetcher-google|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|yandex(?:sitelinks|userproxy))/i ], [NAME, [TYPE, FETCHER]], + ], + + os : [ + [/whatsapp\/[\d\.]+ (a|i)/i], + [[NAME, os => os == 'A' ? 'Android' : 'iOS' ]] ] }); @@ -362,6 +367,9 @@ const Bots = Object.freeze({ ...Crawlers.browser, ...Fetchers.browser, ...Libraries.browser + ], + os : [ + ...Fetchers.os ] }); diff --git a/test/unit/extensions.js b/test/unit/extensions.js index 417ed8a59..9f1fa9630 100644 --- a/test/unit/extensions.js +++ b/test/unit/extensions.js @@ -61,6 +61,12 @@ describe('Extensions', () => { major: '1', type: 'fetcher' }); + + const whatsapp = "WhatsApp/2.0 A"; + assert.deepEqual(new UAParser(whatsapp, Fetchers).getOS(), { + name : 'Android', + version : undefined + }); }); describe('Merge', () => { From c6f8ba3788e4348e56063642d62ef7d809888524 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Mar 2025 17:57:47 +0700 Subject: [PATCH 343/388] Add new device: Facebook Portal TV --- src/main/ua-parser.js | 2 ++ test/data/ua/device/facebook.json | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 4966699f0..13bb4cc58 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -777,6 +777,8 @@ ], [[MODEL, CHROMECAST+' Nest Hub'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ /crkey/i // Google Chromecast, Linux-based or unknown ], [[MODEL, CHROMECAST], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /(portaltv)/i // Facebook Portal TV + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, SMARTTV]], [ /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ /(shield \w+ tv)/i // Nvidia Shield TV diff --git a/test/data/ua/device/facebook.json b/test/data/ua/device/facebook.json index 01e1c97ec..f08f81f31 100644 --- a/test/data/ua/device/facebook.json +++ b/test/data/ua/device/facebook.json @@ -34,5 +34,14 @@ "model": "Quest Pro", "type": "xr" } + }, + { + "desc": "Portal TV", + "ua": "Mozilla/5.0 (Linux; Android 9; PortalTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.120 Mobile Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "PortalTV", + "type": "smarttv" + } } ] \ No newline at end of file From f93cb043f589b15e375458633549e079b2c5ac12 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Mar 2025 18:07:46 +0700 Subject: [PATCH 344/388] Improve device detection for Meta Quest --- src/main/ua-parser.js | 2 +- test/unit/helpers.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 13bb4cc58..517200758 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -847,7 +847,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ /(pico) (4|neo3(?: link|pro)?)/i // Pico ], [VENDOR, MODEL, [TYPE, XR]], [ - /; (quest( \d| pro)?)/i // Oculus Quest + /(quest( \d| pro)?s?).+vr/i // Meta Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ /////////////////// diff --git a/test/unit/helpers.js b/test/unit/helpers.js index 95ce87156..127133bec 100644 --- a/test/unit/helpers.js +++ b/test/unit/helpers.js @@ -9,13 +9,11 @@ describe('getDeviceVendor', () => { const modelSM = 'SM-A605G'; const modelRedmi = 'Redmi Note 8'; const modelNexus = 'Nexus 6P'; - const modelQuest = 'Quest 3'; const modelAquos = 'AQUOS-TVX19B'; assert.equal(getDeviceVendor(modelSM), 'Samsung'); assert.equal(getDeviceVendor(modelRedmi), 'Xiaomi'); assert.equal(getDeviceVendor(modelNexus), 'Huawei'); - assert.equal(getDeviceVendor(modelQuest), 'Facebook'); assert.equal(getDeviceVendor(modelAquos), 'Sharp'); }); }); From 6a41513df4f92359a2917b1349372c372da6d1c7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 11 Mar 2025 22:39:21 +0700 Subject: [PATCH 345/388] Add new device vendor: BLU --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 7 ++- test/data/ua/device/alcatel.json | 11 +++- test/data/ua/device/blu.json | 101 +++++++++++++++++++++++++++++++ test/data/ua/device/hmd.json | 9 +++ test/data/ua/device/tcl.json | 9 +++ 7 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 test/data/ua/device/blu.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 9ea1ffec8..907f46962 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -16,6 +16,7 @@ export const Browser: Readonly<{ BAIDU: "Baidu Browser"; BASILISK: "Basilisk"; BLAZER: "Blazer"; + BLU: "BLU"; BOLT: "Bolt"; BOWSER: "Bowser"; BRAVE: "Brave"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 090035792..d28596d57 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -21,6 +21,7 @@ const Browser = Object.freeze({ BAIDU: 'Baidu Browser', BASILISK: 'Basilisk', BLAZER: 'Blazer', + BLU: 'BLU', BOLT: 'Bolt', BOWSER: 'Bowser', BRAVE: 'Brave', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 517200758..f950c5d16 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -585,8 +585,11 @@ /\b(opd2(\d{3}a?))(?: bui|\))/i ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [ + // BLU Vivo Series + /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i + ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ // Vivo - /vivo (\w+)(?: bui|\))/i, + /; vivo (\w+)(?: bui|\))/i, /\b(v[12]\d{3}\w?[at])(?: bui|;)/i ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [ @@ -728,7 +731,7 @@ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (hmd|imo) ([\w ]+?)(?: bui|\))/i, // HMD/IMO + /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo diff --git a/test/data/ua/device/alcatel.json b/test/data/ua/device/alcatel.json index edfdfcda3..3cd613ee9 100644 --- a/test/data/ua/device/alcatel.json +++ b/test/data/ua/device/alcatel.json @@ -1,6 +1,15 @@ [ { - "desc": "Alcatel", + "desc": "Alcatel 4056W", + "ua": "Mozilla/5.0 (Mobile; ALCATEL 4056W; rv:84.0) Gecko/84.0 Firefox/84.0 KAIOS/3.0", + "expect": { + "vendor": "ALCATEL", + "model": "4056W", + "type": "mobile" + } + }, + { + "desc": "Alcatel A564C", "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ALCATEL A564C Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36", "expect": { "vendor": "ALCATEL", diff --git a/test/data/ua/device/blu.json b/test/data/ua/device/blu.json new file mode 100644 index 000000000..969d08682 --- /dev/null +++ b/test/data/ua/device/blu.json @@ -0,0 +1,101 @@ +[ + { + "desc": "BLU Grand X LTE", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BLU Grand X LTE Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "Grand X LTE", + "type": "mobile" + } + }, + { + "desc": "BLU Neo Energy Mini", + "ua": "Mozilla/5.0 (Linux; Android 10; BLU_NEO_ENERGY_MINI Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.54 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "NEO_ENERGY_MINI", + "type": "mobile" + } + }, + { + "desc": "BLU NEO X PLUS", + "ua": "Mozilla/5.0 (Linux; Android 5.1; BLU NEO X PLUS Build/N090U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "NEO X PLUS", + "type": "mobile" + } + }, + { + "desc": "BLU STUDIO X MINI", + "ua": "Mozilla/5.0 (Linux; Android 5.1; BLU STUDIO X MINI Build/S0150UU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "STUDIO X MINI", + "type": "mobile" + } + }, + { + "desc": "BLU Tank Mega", + "ua": "Mozilla/5.0 (Mobile; BLU_TankMega_3G; rv:48.0; CAEN) Gecko/48.0 Firefox/48.0 KAIOS/2.5.1.1", + "expect": { + "vendor": "BLU", + "model": "TankMega_3G", + "type": "mobile" + } + }, + { + "desc": "BLU TOUCHBOOK G7", + "ua": "Mozilla/5.0 (Linux; Android 5.0; BLU TOUCHBOOK G7 Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.107 Safari/537.36 OPR/29.0.1809.91837", + "expect": { + "vendor": "BLU", + "model": "TOUCHBOOK G7", + "type": "mobile" + } + }, + { + "desc": "BLU Vivo 5 Mini", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Vivo 5 Mini Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "Vivo 5 Mini", + "type": "mobile" + } + }, + { + "desc": "BLU VIVO AIR LTE", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; BLU VIVO AIR LTE Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.107 Mobile Safari/537.36 OPR/29.0.1809.91837", + "expect": { + "vendor": "BLU", + "model": "VIVO AIR LTE", + "type": "mobile" + } + }, + { + "desc": "BLU VIVO GO", + "ua": "Mozilla/5.0 (Linux; U; Android 9; VIVO GO Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/72.0.3626.121 Mobile Safari/537.36 OPR/50.0.2254.149182", + "expect": { + "vendor": "BLU", + "model": "VIVO GO", + "type": "mobile" + } + }, + { + "desc": "BLU Vivo One Plus 2019", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Vivo One Plus 2019 Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/277.0.0.41.126;]", + "expect": { + "vendor": "BLU", + "model": "Vivo One Plus 2019", + "type": "mobile" + } + }, + { + "desc": "BLU VIVO SELFIE", + "ua": "Mozilla/5.0 (Linux; U; Android 5.0; es-LA; BLU VIVO SELFIE Build/LRX21M) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/11.3.5.972 U3/0.8.0 Mobile Safari/534.30", + "expect": { + "vendor": "BLU", + "model": "VIVO SELFIE", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/hmd.json b/test/data/ua/device/hmd.json index 2ca10a1ed..a7d38e2f3 100644 --- a/test/data/ua/device/hmd.json +++ b/test/data/ua/device/hmd.json @@ -1,4 +1,13 @@ [ + { + "desc": "HMD Barbie Phone", + "ua": "Mozilla/5.0 (Mobile; HMD Barbie Phone; rv:84.0) Gecko/84.0 Firefox/84.0 KAIOS/3.1", + "expect": { + "vendor": "HMD", + "model": "Barbie Phone", + "type": "mobile" + } + }, { "desc": "HMD Pulse", "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", diff --git a/test/data/ua/device/tcl.json b/test/data/ua/device/tcl.json index 1ea07a31c..8c805371d 100644 --- a/test/data/ua/device/tcl.json +++ b/test/data/ua/device/tcl.json @@ -296,6 +296,15 @@ "type": "mobile" } }, + { + "desc": "TCL 4056S", + "ua": "Mozilla/5.0 (Mobile; TCL 4056S; rv:84.0) Gecko/84.0 Firefox/84.0 KAIOS/3.0", + "expect": { + "vendor": "TCL", + "model": "4056S", + "type": "mobile" + } + }, { "desc": "TCL A3", "ua": "Mozilla/5.0 (Linux; Android 11; A509DL Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 GSA/13.18.7.23.arm64", From af8acf9078e5952a496d395464af8178ad55ccaf Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 15 Mar 2025 00:37:56 +0700 Subject: [PATCH 346/388] Bump version `2.0.3` --- CHANGELOG.md | 18 + dist/ua-parser.min.js | 6 +- dist/ua-parser.min.mjs | 6 +- dist/ua-parser.pack.js | 6 +- dist/ua-parser.pack.mjs | 6 +- package-lock.json | 1658 +--------------------- package.json | 2 +- src/enums/ua-parser-enums.d.ts | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 10 +- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 65 +- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 5 +- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 6 +- src/main/ua-parser.mjs | 48 +- 19 files changed, 167 insertions(+), 1683 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f62d6ac58..9f2fa9b76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,24 @@ --- +## Version 2.0.3 + +- Add new browser: Dooble, Ecosia, LG Browser, Otter, qutebrowser, Surf +- Add new device: BLU, Facebook Portal TV +- Improve device detection: Archos, LG, Meta Quest +- Remove jazzer.js fuzz test +- Improve `withClientHints()`: + - Browser naming adjustments: + - `HuaweiBrowser` => `Huawei Browser` + - `Miui Browser` => `MIUI Browser` + - `OperaMobile` => `Opera Mobi` + - `YaBrowser` => `Yandex` +- `extensions` submodule: + - Add new Crawler: AdIdxBot, Linespider, LinkedInBot, OpenAI Image Downloader, SemrushBot, Yahoo! Slurp + - Add new Fetcher: Better Uptime Bot, Google-PageRenderer, GoogleImageProxy, MicrosoftPreview, Snap URL Preview, SkypeUriPreview, TelegramBot + - Add new Vehicles: BMW, Jeep + - Add OS detection of WhatsApp user-agent + ## Version 2.0.2 - Fix TypeScript dependency issue diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 02d8a82bc..0c7f4071e 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.2 - Copyright © 2012-2024 Faisal Salman +/* UAParser.js v2.0.3 + Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.2",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.3",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome","Huawei Browser":"HuaweiBrowser","MIUI Browser":"Miui Browser","Opera Mobi":"OperaMobile",Yandex:"YaBrowser"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.min.mjs b/dist/ua-parser.min.mjs index 6def7c8c6..b86273f46 100644 --- a/dist/ua-parser.min.mjs +++ b/dist/ua-parser.min.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.2 - Copyright © 2012-2024 Faisal Salman +/* UAParser.js v2.0.3 + Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -var LIBVERSION="2.0.2",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file +var LIBVERSION="2.0.3",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome","Huawei Browser":"HuaweiBrowser","MIUI Browser":"Miui Browser","Opera Mobi":"OperaMobile",Yandex:"YaBrowser"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 6807b0eac..ab102f131 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.2 - Copyright © 2012-2024 Faisal Salman +/* UAParser.js v2.0.3 + Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -((i,l)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Pi,e):Pi,N.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Ri(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Ri(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>M?Hi(i,M):i),this}]]).setUA(r),this):new P(i,e,t).getResult()}P.VERSION="2.0.2",P.BROWSER=I([v,y,B,k]),P.CPU=I([C]),P.DEVICE=I([T,x,k,G,S,e,r,t,D]),P.ENGINE=P.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=P:exports).UAParser=P:typeof define===V&&define.amd?define(function(){return P}):Ti&&(i.UAParser=P);var Vi,Li=Ti&&(i.jQuery||i.Zepto);Li&&!Li.ua&&(Vi=new P,Li.ua=Vi.getResult(),Li.ua.get=function(){return Vi.getUA()},Li.ua.set=function(i){Vi.setUA(i);var e,t=Vi.getResult();for(e in t)Li.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file +((i,l)=>{function U(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ii,e):Ii,j.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Bi(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Bi(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>E?Hi(i,E):i),this}]]).setUA(r),this):new I(i,e,t).getResult()}I.VERSION="2.0.3",I.BROWSER=U([v,y,L,k]),I.CPU=U([C]),I.DEVICE=U([T,x,k,G,S,e,r,t,D]),I.ENGINE=I.OS=U([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=I:exports).UAParser=I:typeof define===R&&define.amd?define(function(){return I}):Ti&&(i.UAParser=I);var Ri,Vi=Ti&&(i.jQuery||i.Zepto);Vi&&!Vi.ua&&(Ri=new I,Vi.ua=Ri.getResult(),Vi.ua.get=function(){return Ri.getUA()},Vi.ua.set=function(i){Ri.setUA(i);var e,t=Ri.getResult();for(e in t)Vi.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.mjs b/dist/ua-parser.pack.mjs index 5cc94b76e..6c1624036 100644 --- a/dist/ua-parser.pack.mjs +++ b/dist/ua-parser.pack.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.2 - Copyright © 2012-2024 Faisal Salman +/* UAParser.js v2.0.3 + Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -function M(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ei,e):Ei,N.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Ri(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Ri(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return A(i)&&(r=i.length>P?Ai(i,P):i),this}]]).setUA(r),this):new I(i,e,t).getResult()}I.VERSION="2.0.2",I.BROWSER=M([g,x,C,v]),I.CPU=M([y]),I.DEVICE=M([T,k,v,G,S,i,r,e,F]),I.ENGINE=I.OS=M([g,x]);export{I as UAParser}; \ No newline at end of file +function E(i){for(var e={},o=0;o{var o,t={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(o in i)t[o]=r[o]&&r[o].length%2==0?r[o].concat(i[o]):i[o];return t})(Ii,e):Ii,M.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Pi(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Pi(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return A(i)&&(r=i.length>B?Ai(i,B):i),this}]]).setUA(r),this):new j(i,e,o).getResult()}j.VERSION="2.0.3",j.BROWSER=E([g,x,C,v]),j.CPU=E([y]),j.DEVICE=E([T,k,v,L,S,i,r,e,F]),j.ENGINE=j.OS=E([g,x]);export{j as UAParser}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 187bf0243..70cf43945 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.2", + "version": "2.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.2", + "version": "2.0.3", "funding": [ { "type": "opencollective", @@ -35,7 +35,6 @@ "devDependencies": { "@babel/parser": "7.15.8", "@babel/traverse": "7.23.2", - "@jazzer.js/core": "^1.4.0", "@playwright/test": "^1.49.0", "jshint": "~2.13.6", "mocha": "~8.2.0", @@ -48,19 +47,6 @@ "node": "*" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -75,78 +61,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.26.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/generator": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", @@ -178,22 +92,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", @@ -231,102 +129,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports/node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.26.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-module-imports/node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.26.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", @@ -357,28 +159,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/parser": { "version": "7.15.8", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", @@ -469,108 +249,6 @@ "node": ">=6.9.0" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jazzer.js/bug-detectors": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/bug-detectors/-/bug-detectors-1.7.0.tgz", - "integrity": "sha512-ytGXkiH0G0XWXt1sm2KKdSWwEpOIN+SIe2wT6uvIO2mrUqQmLL2LnINeahL5T8L2b0+dafYDGZeVxElgsgH5eA==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "dependencies": { - "@jazzer.js/core": "1.7.0", - "@jazzer.js/hooking": "1.7.0" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, - "node_modules/@jazzer.js/core": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/core/-/core-1.7.0.tgz", - "integrity": "sha512-HGSNb8HxPjK/z84I3CZUmXd/+TqTfjjGOgXKzHK9/fmwUltLLuUcbSTkc1RDHrgT4z2EF7Tlkeo51Vp648f8Ag==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "dependencies": { - "@jazzer.js/bug-detectors": "1.7.0", - "@jazzer.js/fuzzer": "1.7.0", - "@jazzer.js/hooking": "1.7.0", - "@jazzer.js/instrumentor": "1.7.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.1.6", - "tmp": "^0.2.1", - "yargs": "^17.7.2" - }, - "bin": { - "jazzer": "dist/cli.js" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, - "node_modules/@jazzer.js/fuzzer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/fuzzer/-/fuzzer-1.7.0.tgz", - "integrity": "sha512-FDTsHzUQva3G6ETowxAlBNY+aXc4J+XzKcTRtxQdIIW9wPli2qxUjlVCkPHfKCfOcdNseAygR/AIfrXfrB0iWw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "bindings": "^1.5.0", - "cmake-js": "^7.2.1", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, - "node_modules/@jazzer.js/hooking": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/hooking/-/hooking-1.7.0.tgz", - "integrity": "sha512-1uGhdaRVAyNTnCuThcYFnjKKWIlVnobM+LUVoRHAFe+TVFyfnV2wCKCjoj+kjZtt4fD0lL9gjkKSafbiKNzsnA==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "dependencies": { - "@babel/core": "^7.22.11" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, - "node_modules/@jazzer.js/instrumentor": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@jazzer.js/instrumentor/-/instrumentor-1.7.0.tgz", - "integrity": "sha512-YPMzL0mH8+UKGwe0r/7vhq3DsxHOxhkeqTusYTajDycwz6/51y3JC20NhX3qs1Jyd7xpIo7ANhVN0dU4V1MwMw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "dependencies": { - "@babel/core": "^7.22.11", - "@babel/generator": "^7.22.10", - "@jazzer.js/fuzzer": "1.7.0", - "@jazzer.js/hooking": "1.7.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "proper-lockfile": "^4.1.2", - "source-map-support": "^0.5.21" - }, - "engines": { - "node": ">= 14.0.0", - "npm": ">= 7.0.0" - } - }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -814,61 +492,6 @@ "node": ">= 8" } }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -901,43 +524,12 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/axios": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", - "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -950,49 +542,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bl/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1021,68 +570,6 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1109,26 +596,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001684", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", - "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1166,15 +633,6 @@ "fsevents": "~2.1.2" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/cli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", @@ -1188,59 +646,6 @@ "node": ">=0.2.5" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cmake-js": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.3.0.tgz", - "integrity": "sha512-dXs2zq9WxrV87bpJ+WbnGKv8WUBXDw8blNiwNHoRe/it+ptscxhQHKB1SJXa1w+kocLMeP28Tk4/eTCezg4o+w==", - "dev": true, - "dependencies": { - "axios": "^1.6.5", - "debug": "^4", - "fs-extra": "^11.2.0", - "lodash.isplainobject": "^4.0.6", - "memory-stream": "^1.0.0", - "node-api-headers": "^1.1.0", - "npmlog": "^6.0.2", - "rc": "^1.2.7", - "semver": "^7.5.4", - "tar": "^6.2.0", - "url-join": "^4.0.1", - "which": "^2.0.2", - "yargs": "^17.7.2" - }, - "bin": { - "cmake-js": "bin/cmake-js" - }, - "engines": { - "node": ">= 14.15.0" - } - }, - "node_modules/cmake-js/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1259,15 +664,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1294,18 +690,6 @@ "date-now": "^0.1.4" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -1369,45 +753,6 @@ "node": ">=0.10.0" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/default-require-extensions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1416,12 +761,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true - }, "node_modules/detect-europe-js": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.2.tgz", @@ -1441,15 +780,6 @@ } ] }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1536,27 +866,12 @@ "domelementtype": "1" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.67", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", - "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==", - "dev": true - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/entities": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", @@ -1572,15 +887,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1643,15 +949,6 @@ "node": ">= 0.8.0" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -1677,132 +974,56 @@ "reusify": "^1.0.4" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=14.14" + "node": ">=8" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dependencies": { - "yallist": "^4.0.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1832,44 +1053,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/gauge/node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1879,12 +1062,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1959,12 +1136,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -1992,12 +1163,6 @@ "node": ">=8" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2049,12 +1214,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, "node_modules/htmlparser2": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", @@ -2068,26 +1227,6 @@ "readable-stream": "1.1" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2123,12 +1262,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, "node_modules/irregular-plurals": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", @@ -2250,97 +1383,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.26.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", @@ -2420,30 +1462,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -2480,12 +1498,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true - }, "node_modules/log-symbols": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", @@ -2498,42 +1510,6 @@ "node": ">=10" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -2546,38 +1522,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/memory-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", - "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", - "dev": true, - "dependencies": { - "readable-stream": "^3.4.0" - } - }, - "node_modules/memory-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/memory-stream/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/meow": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", @@ -2666,18 +1610,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -2699,87 +1631,20 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minimist-options": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, "node_modules/mocha": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", @@ -3098,48 +1963,6 @@ "node": "^10 || ^12 || >=13.7" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "dev": true - }, - "node_modules/node-abi": { - "version": "3.71.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", - "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true - }, - "node_modules/node-api-headers": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.4.0.tgz", - "integrity": "sha512-u83U3WnRbBpWlhc0sQbpF3slHRLV/a6/OXByc+QzHcLxiDiJUWLuKGZp4/ntZUchnXGOCnCq++JUEtwb1/tyow==", - "dev": true - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -3159,12 +1982,6 @@ } } }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true - }, "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -3201,22 +2018,6 @@ "node": ">=0.10.0" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3393,32 +2194,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "dev": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -3445,33 +2220,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3510,30 +2258,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -3760,15 +2484,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3831,15 +2546,6 @@ "regexp-tree": "~0.1.1" } }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/serialize-javascript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", @@ -3855,57 +2561,6 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3915,25 +2570,6 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -4004,15 +2640,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -4074,95 +2701,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tar-stream/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "dev": true, - "engines": { - "node": ">=14.14" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4210,18 +2748,6 @@ "node": ">=14.16" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -4270,57 +2796,6 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4424,62 +2899,12 @@ "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", "dev": true }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/yargs-parser": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", @@ -4538,15 +2963,6 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index d6a3dc1d8..ba1f89d1c 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.2", + "version": "2.0.3", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 907f46962..2fb0db5eb 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Enums submodule of UAParser.js v2.0.2 +// Type definitions for Enums submodule of UAParser.js v2.0.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index d28596d57..0a53cceb6 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.2 +/* Enums for UAParser.js v2.0.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 6e8257aa6..571eb70c5 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.2 +/* Enums for UAParser.js v2.0.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -25,6 +25,7 @@ const Browser = Object.freeze({ BAIDU: 'Baidu Browser', BASILISK: 'Basilisk', BLAZER: 'Blazer', + BLU: 'BLU', BOLT: 'Bolt', BOWSER: 'Bowser', BRAVE: 'Brave', @@ -41,9 +42,11 @@ const Browser = Object.freeze({ DAUM: 'Daum', DILLO: 'Dillo', DOLPHIN: 'Dolphin', + DOOBLE: 'Dooble', DORIS: 'Doris', DRAGON: 'Dragon', DUCKDUCKGO: 'DuckDuckGo', + ECOSIA: 'Ecosia', EDGE: 'Edge', EPIPHANY: 'Epiphany', FACEBOOK: 'Facebook', @@ -82,6 +85,7 @@ const Browser = Object.freeze({ KINDLE: 'Kindle', LENOVO: 'Smart Lenovo Browser', LADYBIRD: 'Ladybird', + LG: 'LG Browser', LIBREWOLF: 'LibreWolf', LIEBAO: 'LBBROWSER', LINE: 'Line', @@ -106,10 +110,12 @@ const Browser = Object.freeze({ OMNIWEB: 'OmniWeb', OPERA: 'Opera', OPERA_COAST: 'Opera Coast', + OPERA_GX: 'Opera GX', OPERA_MINI: 'Opera Mini', OPERA_MOBI: 'Opera Mobi', OPERA_TABLET: 'Opera Tablet', OPERA_TOUCH: 'Opera Touch', + OTTER: 'Otter', OVI: 'OviBrowser', PALEMOON: 'PaleMoon', PHANTOMJS: 'PhantomJS', @@ -121,6 +127,7 @@ const Browser = Object.freeze({ QQ_LITE: 'QQBrowserLite', QUARK: 'Quark', QUPZILLA: 'QupZilla', + QUTEBROWSER: 'qutebrowser', REKONQ: 'rekonq', ROCKMELT: 'Rockmelt', SAFARI: 'Safari', @@ -137,6 +144,7 @@ const Browser = Object.freeze({ SNAPCHAT: 'Snapchat', SOGOU_EXPLORER: 'Sogou Explorer', SOGOU_MOBILE: 'Sogou Mobile', + SURF: 'Surf', SWIFTFOX: 'Swiftfox', TESLA: 'Tesla', TIKTOK: 'TikTok', diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 500a0a2aa..3b9b63a98 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.2 +// Type definitions for Helpers submodule of UAParser.js v2.0.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index e8e3f6e80..6665b6d23 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.2 +/* Extensions for UAParser.js v2.0.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 8e7bc88fd..9770ebc54 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.2 +/* Extensions for UAParser.js v2.0.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -46,25 +46,25 @@ const Crawlers = Object.freeze({ [ // AhrefsBot - https://ahrefs.com/robot // Amazonbot - https://developer.amazon.com/amazonbot - // Bingbot - http://www.bing.com/bingbot.htm + // Bingbot / AdIdxBot - https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0 // CCBot - https://commoncrawl.org/faq // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot + // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot - // SemrushBot - http://www.semrush.com/bot.html // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot - /(applebot(?:-extended)?)\/([\w\.]+)/i, + /(applebot(?:-extended)?)\/?([\w\.]*)/i, // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 - /(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i, + /(baiduspider[-imagevdonwsfcpr]{0,7})\/?([\w\.]*)/i, // ClaudeBot (Anthropic) /(claude(?:bot|-web)|anthropic-ai)\/?([\w\.]*)/i, @@ -82,6 +82,9 @@ const Crawlers = Object.freeze({ // Internet Archive (archive.org) /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + // SemrushBot - http://www.semrush.com/bot.html + /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, + // Sogou Spider /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, @@ -94,8 +97,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Diffbot / Magpie-Crawler / Omgilibot / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |line|yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -112,7 +115,7 @@ const Crawlers = Object.freeze({ // Qihoo 360Spider // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|(?=yahoo! )slurp)/i + /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|yahoo! slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -221,27 +224,35 @@ const Fetchers = Object.freeze({ // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // DuckAssistBot - https://duckduckgo.com/duckassistbot/ - // BingPreview / Mastodon / Pinterestbot / Redditbot / Rogerbot / Telegrambot / Twitterbot / UptimeRobot + // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|bingpreview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|telegram|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, + // Skype + /(skypeuripreview) preview\/([\w\.]+)/i, + // Slackbot - https://api.slack.com/robots /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, // WhatsApp - /(whatsapp)\/([\w\.]+)[\/ ][ianw]/i + /(whatsapp)\/([\w\.]+)/i ], [NAME, VERSION, [TYPE, FETCHER]], [ // Google Bots / Cohere / Snapchat / Vercelbot / Yandex Bots - /(cohere-ai|vercelbot|feedfetcher-google|google(?:-read-aloud|producer)|(?=bot; )snapchat|yandex(?:sitelinks|userproxy))/i + /((?:better uptime |telegram|vercel)bot|cohere-ai|feedfetcher-google|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|yandex(?:sitelinks|userproxy))/i ], [NAME, [TYPE, FETCHER]], + ], + + os : [ + [/whatsapp\/[\d\.]+ (a|i)/i], + [[NAME, os => os == 'A' ? 'Android' : 'iOS' ]] ] }); @@ -252,10 +263,12 @@ const Fetchers = Object.freeze({ const InApps = Object.freeze({ browser : [ // Slack - [/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]], + [/(?:slack(?=.+electron|.+ios)|chatlyio)\/([\d\.]+)/i], + [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], // Yahoo! Japan - [/jp\.co\.yahoo\.android\.yjtop\/([\d\.]+)/i], [VERSION, 'Yahoo! Japan', [TYPE, INAPP]] + [/jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], + [VERSION, [NAME, 'Yahoo! Japan'], [TYPE, INAPP]] ] }); @@ -331,15 +344,20 @@ const Libraries = Object.freeze({ const Vehicles = Object.freeze({ device : [ - [ - /dilink.+(byd) auto/i, // BYD - ], [VENDOR], [ + [/aftlbt962e2/i], // BMW + [[VENDOR, 'BMW']], + + [/dilink.+(byd) auto/i], // BYD + [VENDOR], - /(rivian) (r1t)/i, // Rivian - ], [VENDOR, MODEL], [ + [/aftlft962x3/i], // Jeep + [[VENDOR, 'Jeep'], [MODEL, 'Wagooner']], + + [/(rivian) (r1t)/i], // Rivian + [VENDOR, MODEL], - /vcc.+netfront/i, // Volvo - ], [[VENDOR, 'Volvo']] + [/vcc.+netfront/i], // Volvo + [[VENDOR, 'Volvo']] ] }); @@ -353,6 +371,9 @@ const Bots = Object.freeze({ ...Crawlers.browser, ...Fetchers.browser, ...Libraries.browser + ], + os : [ + ...Fetchers.os ] }); diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 389e8c675..4b6c80813 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.2 +// Type definitions for Helpers submodule of UAParser.js v2.0.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index daaa89792..64d7e90db 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.2 +/* Helpers for UAParser.js v2.0.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index 622e27a62..e47aae4d8 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.2 +/* Helpers for UAParser.js v2.0.3 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -97,6 +97,9 @@ const isAIBot = (resultOrUA) => [ // Perplexity 'perplexitybot', + // Semrush + 'semrushbot-ocob', + // Timpi 'timpibot', diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index c22a3de53..6c7c9f8a1 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.2 +// Type definitions for UAParser.js v2.0.3 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index f950c5d16..a6f1091f2 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.2 - Copyright © 2012-2024 Faisal Salman +/* UAParser.js v2.0.3 + Copyright © 2012-2025 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.2', + var LIBVERSION = '2.0.3', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 0dfa143cf..00eb9bbcf 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,8 +3,8 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.2 - Copyright © 2012-2024 Faisal Salman +/* UAParser.js v2.0.3 + Copyright © 2012-2025 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Supports browser & node.js environment. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.2', + var LIBVERSION = '2.0.3', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', @@ -355,11 +355,11 @@ /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer - // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon - /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon)\/([-\w\.]+)/i, + // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser)\/([-\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon - /(heytap|ovi|115)browser\/([\d\.]+)/i, // HeyTap/Ovi/115 - /(weibo)__([\d\.]+)/i // Weibo + /(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf + /(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo ], [NAME, VERSION], [ /quark(?:pc)?\/([-\w\.]+)/i // Quark ], [VERSION, [NAME, 'Quark']], [ @@ -587,8 +587,11 @@ /\b(opd2(\d{3}a?))(?: bui|\))/i ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [ + // BLU Vivo Series + /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i + ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ // Vivo - /vivo (\w+)(?: bui|\))/i, + /; vivo (\w+)(?: bui|\))/i, /\b(v[12]\d{3}\w?[at])(?: bui|;)/i ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [ @@ -608,7 +611,7 @@ /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, - /\blg[-e;\/ ]+((?!browser|netcast|android tv|watch)\w+)/i, + /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch))(\w+)/i, /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ @@ -715,6 +718,14 @@ /droid.+; (a(?:015|06[35]|142p?))/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ + // Archos + /; (x67 5g|tikeasy \w+|ac[1789]\d\w+)( b|\))/i, + /archos ?(5|gamepad2?|([\w ]*[t1789]|hello) ?\d+[\w ]*)( b|\))/i + ], [MODEL, [VENDOR, 'Archos'], [TYPE, TABLET]], [ + /archos ([\w ]+)( b|\))/i, + /; (ac[3-6]\d\w{2,8})( b|\))/i + ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [ + // MIXED /(imo) (tab \w+)/i, // IMO /(infinix) (x1101b?)/i // Infinix XPad @@ -722,7 +733,7 @@ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (hmd|imo) ([\w ]+?)(?: bui|\))/i, // HMD/IMO + /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo @@ -730,7 +741,6 @@ ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /(kobo)\s(ereader|touch)/i, // Kobo - /(archos) (gamepad2?)/i, // Archos /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad /(kindle)\/([\w\.]+)/i // Kindle ], [VENDOR, MODEL, [TYPE, TABLET]], [ @@ -758,7 +768,9 @@ ], [VENDOR, [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ - /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV + /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ + /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i ], [[VENDOR, LG], [TYPE, SMARTTV]], [ /(apple) ?tv/i // Apple TV ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ @@ -770,6 +782,8 @@ ], [[MODEL, CHROMECAST+' Nest Hub'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ /crkey/i // Google Chromecast, Linux-based or unknown ], [[MODEL, CHROMECAST], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /(portaltv)/i // Facebook Portal TV + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, SMARTTV]], [ /droid.+aft(\w+)( bui|\))/i // Fire TV ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ /(shield \w+ tv)/i // Nvidia Shield TV @@ -838,7 +852,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ /(pico) (4|neo3(?: link|pro)?)/i // Pico ], [VENDOR, MODEL, [TYPE, XR]], [ - /; (quest( \d| pro)?)/i // Oculus Quest + /(quest( \d| pro)?s?).+vr/i // Meta Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ /////////////////// @@ -933,7 +947,7 @@ /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen - /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/; ]?([\d\.]*)/i + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i ], [NAME, VERSION], [ /\(bb(10);/i // BlackBerry 10 ], [VERSION, [NAME, BLACKBERRY]], [ @@ -1215,7 +1229,11 @@ 'Chrome' : 'Google Chrome', 'Edge' : 'Microsoft Edge', 'Chrome WebView' : 'Android WebView', - 'Chrome Headless' : 'HeadlessChrome' + 'Chrome Headless' : 'HeadlessChrome', + 'Huawei Browser' : 'HuaweiBrowser', + 'MIUI Browser' : 'Miui Browser', + 'Opera Mobi' : 'OperaMobile', + 'Yandex' : 'YaBrowser' }); this.set(NAME, brandName) .set(VERSION, brandVersion) From cd3bef74058d6428033606b286a45c59480c6356 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 27 Mar 2025 10:25:44 +0700 Subject: [PATCH 347/388] Improve OS detection: HarmonyOS, MorphOS --- src/main/ua-parser.js | 9 ++++--- test/data/ua/os/harmonyos.json | 47 +++++++++++++++++++++++++++++++++- test/data/ua/os/morphos.json | 27 +++++++++++++++++++ 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a6f1091f2..5c4cc7833 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -924,7 +924,7 @@ /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, - /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS + /(macintosh|mac_powerpc\b)(?!.+(haiku|morphos))/i // Mac OS ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ // Google Chromecast @@ -940,10 +940,11 @@ ], [VERSION, [NAME, CHROMECAST]], [ // Mobile OSes - /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS + /droid ([\w\.]+)\b.+(android[- ]x86)/i // Android-x86 ], [VERSION, NAME], [ /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ + /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i ], [NAME, VERSION], [ @@ -979,7 +980,7 @@ /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire - /(hurd|linux)(?: arm\w*| x86\w*| ?)([\w\.]*)/i, // Hurd/Linux + /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS /(gnu) ?([\w\.]*)/i, // GNU /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly /(haiku) (\w+)/i // Haiku @@ -988,7 +989,7 @@ ], [[NAME, 'Solaris'], VERSION], [ /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX - /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS + /\b(beos|os\/2|amigaos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/Fuchsia/HP-UX/SerenityOS /(unix) ?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] diff --git a/test/data/ua/os/harmonyos.json b/test/data/ua/os/harmonyos.json index 1fa13efed..c2016541d 100644 --- a/test/data/ua/os/harmonyos.json +++ b/test/data/ua/os/harmonyos.json @@ -5,7 +5,52 @@ "expect" : { "name" : "HarmonyOS", - "version" : "10" + "version" : "undefined" + } + }, + { + "desc" : "HarmonyOS 2", + "ua" : "Mozilla/5.0 (Linux; Android 10; STK-AL00 Build/HUAWEISTK-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.93 Mobile Safari/537.36 BDOS/1.0 (HarmonyOS 2.2.0) SP-engine/2.72.0 baiduboxapp/13.34.5.10 (Baidu; P1 10) NABar/1.0", + "expect" : + { + "name" : "HarmonyOS", + "version" : "2.2.0" + } + }, + { + "desc" : "HarmonyOS 3", + "ua" : "Mozilla/5.0 (Linux; Android 12; Huawei P60 Pro Build/HarmonyOS 3.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.1.6136.139 Mobile Safari/537.36 OPR/111.11", + "expect" : + { + "name" : "HarmonyOS", + "version" : "3.1.0" + } + }, + { + "desc" : "HarmonyOS 4", + "ua" : "Mozilla/5.0 (Android 12; HarmonyOS 4.0.0.118; OXF-AN00 Build/HUAWEIOXF-AN00; HMSCore 6.13.0.302; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36 EdgA/125.0.0.0 Language/zh_TW ABI/arm64-v8a", + "expect" : + { + "name" : "HarmonyOS", + "version" : "4.0.0.118" + } + }, + { + "desc" : "HarmonyOS 4", + "ua" : "Mozilla/5.0 (Linux; Android 12; HarmonyOS/4.0.3.601; JAD-AL50; HMSCore/6.13.0.320 Build/AP31.240223.016.A3; x64; ARM64) AppleWebKit/537.36 (KHTML, like Gecko) HuaweiBrowser/14.0.7.302 Mobile Safari/537.36", + "expect" : + { + "name" : "HarmonyOS", + "version" : "4.0.3.601" + } + }, + { + "desc" : "HarmonyOS 5", + "ua" : "Mozilla/5.0 (PC; OpenHarmony 5.0; HarmonyOS 5.0) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Browser/harmony360Browser/1.0.0", + "expect" : + { + "name" : "HarmonyOS", + "version" : "5.0" } } ] \ No newline at end of file diff --git a/test/data/ua/os/morphos.json b/test/data/ua/os/morphos.json index f255eeb27..e540858d9 100644 --- a/test/data/ua/os/morphos.json +++ b/test/data/ua/os/morphos.json @@ -7,5 +7,32 @@ "name" : "MorphOS", "version" : "undefined" } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (Macintosh; PowerPC MorphOS 3.7; Odyssey Web Browser; rv:1.23) AppleWebKit/538.1 (KHTML, like Gecko) OWB/1.23 Safari/538.1", + "expect" : + { + "name" : "MorphOS", + "version" : "3.7" + } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (X11; MorphOS ppc64; rv:88.0) Gecko/20100101 Firefox/88.0", + "expect" : + { + "name" : "MorphOS", + "version" : "undefined" + } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (compatible; Origyn Web Browser; MorphOS; PPC; U) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+)", + "expect" : + { + "name" : "MorphOS", + "version" : "undefined" + } } ] \ No newline at end of file From 8ae88b6aa56eed2c6e0e0a0e30ffa14e2526494c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 28 Mar 2025 05:00:32 +0700 Subject: [PATCH 348/388] Improve OS detection: AIX, Fuchsia, Haiku, Solaris --- src/main/ua-parser.js | 12 ++++++------ test/data/ua/os/aix.json | 27 +++++++++++++++++++++++++++ test/data/ua/os/fuchsia.json | 9 +++++++++ test/data/ua/os/haiku.json | 27 +++++++++++++++++++++++++++ test/data/ua/os/solaris.json | 29 ++++++++++++++++++++++++++++- 5 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 5c4cc7833..d3bd002bf 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -977,19 +977,19 @@ // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm /(mint)[\/\(\) ]?(\w*)/i, // Mint - /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux + /(mageia|vectorlinux|fuchsia)[; ]/i, // Mageia/VectorLinux/Fuchsia /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire + /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris + /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS /(gnu) ?([\w\.]*)/i, // GNU /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly - /(haiku) (\w+)/i // Haiku + /(haiku) ?(r\d)?/i // Haiku ], [NAME, VERSION], [ - /(sunos) ?([\w\.\d]*)/i // Solaris + /(sunos) ?([\d\.]*)/i // Solaris ], [[NAME, 'Solaris'], VERSION], [ - /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris - /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX - /\b(beos|os\/2|amigaos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/Fuchsia/HP-UX/SerenityOS + /\b(beos|os\/2|amigaos|openvms|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/HP-UX/SerenityOS /(unix) ?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] diff --git a/test/data/ua/os/aix.json b/test/data/ua/os/aix.json index 02ccda875..d45d1cbe6 100644 --- a/test/data/ua/os/aix.json +++ b/test/data/ua/os/aix.json @@ -7,5 +7,32 @@ "name" : "AIX", "version" : "undefined" } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (AIX; SPARC64; smart-thumbtack; Mosaic/2.7) Gecko/20100101 Mosaic/2.7", + "expect" : + { + "name" : "AIX", + "version" : "undefined" + } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (X11; N; Linux i686; en-US; rv:1.8.1.12) Gecko/20080219 Firefox/2.0.0.12 Mozilla/3.0 (X11; I; AIX 2) Navigator/9.0.0.6", + "expect" : + { + "name" : "AIX", + "version" : "2" + } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (X11; U; AIX 7.2; en-US; rv:1.7.12) Gecko/20100101 Firefox/126.0", + "expect" : + { + "name" : "AIX", + "version" : "7.2" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/fuchsia.json b/test/data/ua/os/fuchsia.json index 0759cba17..1f2e0501d 100644 --- a/test/data/ua/os/fuchsia.json +++ b/test/data/ua/os/fuchsia.json @@ -7,5 +7,14 @@ "name" : "Fuchsia", "version" : "undefined" } + }, + { + "desc" : "Fuchsia", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Mozilla/5.0 (X11; Linux; Fuchsia; GoogleTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Large Screen Safari/537.36 GoogleTV", + "expect" : + { + "name" : "Fuchsia", + "version" : "undefined" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/haiku.json b/test/data/ua/os/haiku.json index 7005087ad..f2537395b 100644 --- a/test/data/ua/os/haiku.json +++ b/test/data/ua/os/haiku.json @@ -7,5 +7,32 @@ "name" : "Haiku", "version" : "R1" } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (X11; Haiku x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (Haiku; BeOS; rv:99.0) Gecko/20100101 Firefox/99.0", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (X11; Haiku BePC) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.17 Chrome/87.0.4280.144 Safari/537.36 Dooble/2023.12.25 Dooble/2023.12.25", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/solaris.json b/test/data/ua/os/solaris.json index 450e47dbf..4c8f6739e 100644 --- a/test/data/ua/os/solaris.json +++ b/test/data/ua/os/solaris.json @@ -5,7 +5,16 @@ "expect" : { "name" : "Solaris", - "version" : "sun4u" + "version" : "undefined" + } + }, + { + "desc" : "Solaris", + "ua" : "Mozilla/5.0 (Solaris; SPARC; w3m/0.5.3; rv:1.0) Gecko/20100101 w3m/0.5.3", + "expect" : + { + "name" : "Solaris", + "version" : "undefined" } }, { @@ -16,5 +25,23 @@ "name" : "Solaris", "version" : "4.1.4" } + }, + { + "desc" : "Solaris", + "ua" : "Opera/5.0 (SunOS 5.8 sun4m; U) [en]", + "expect" : + { + "name" : "Solaris", + "version" : "5.8" + } + }, + { + "desc" : "Solaris", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.8) Gecko/20100215 Solaris/10.1 (GNU) Superswan/3.5.8 (Byte/me)", + "expect" : + { + "name" : "Solaris", + "version" : "10.1" + } } ] \ No newline at end of file From 598c51c69cb6533ab3ab85085f32727ddb416fd3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 28 Mar 2025 05:23:36 +0700 Subject: [PATCH 349/388] Add new OS: ArcaOS https://www.arcanoae.com/arcaos/ --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 2 +- test/data/ua/os/arcaos.json | 20 ++++++++++++++++++++ test/data/ua/os/os2.json | 18 ++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/data/ua/os/arcaos.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 2fb0db5eb..8dff5ea96 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -288,6 +288,7 @@ export const OS: Readonly<{ AMIGA_OS: "Amiga OS"; ANDROID: "Android"; ANDROID_X86: "Android-x86"; + ARCAOS: "ArcaOS"; ARCH: "Arch"; BADA: "Bada"; BEOS: "BeOS"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 0a53cceb6..4bfcec433 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -303,6 +303,7 @@ const OS = Object.freeze({ AMIGA_OS: 'Amiga OS', ANDROID: 'Android', ANDROID_X86: 'Android-x86', + ARCAOS: 'ArcaOS', ARCH: 'Arch', BADA: 'Bada', BEOS: 'BeOS', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d3bd002bf..b3998746f 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -977,7 +977,7 @@ // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm /(mint)[\/\(\) ]?(\w*)/i, // Mint - /(mageia|vectorlinux|fuchsia)[; ]/i, // Mageia/VectorLinux/Fuchsia + /(mageia|vectorlinux|fuchsia|arcaos)[; ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris diff --git a/test/data/ua/os/arcaos.json b/test/data/ua/os/arcaos.json new file mode 100644 index 000000000..5097545b7 --- /dev/null +++ b/test/data/ua/os/arcaos.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "ArcaOS", + "ua" : "Mozilla/5.0 (OS/2; ArcaOS 5.0.6; x86_64; rv:89.0) Gecko/20100101 Firefox/89.0", + "expect" : + { + "name" : "ArcaOS", + "version" : "5.0.6" + } + }, + { + "desc" : "ArcaOS", + "ua" : "Mozilla/5.0 (OS/2; ArcaOS; x64; rv:89.0) Gecko/20100101 Firefox/89.0", + "expect" : + { + "name" : "ArcaOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/os2.json b/test/data/ua/os/os2.json index cb38c1183..56eac7c3f 100644 --- a/test/data/ua/os/os2.json +++ b/test/data/ua/os/os2.json @@ -7,5 +7,23 @@ "name" : "OS/2", "version" : "undefined" } + }, + { + "desc" : "OS/2", + "ua" : "Mozilla/5.0 (OS/2; OS/2 i386) AppleWebKit/538.36 (KHTML, like Gecko) QtWebEngine/5.15.2 Chrome/127.0.6533.72", + "expect" : + { + "name" : "OS/2", + "version" : "undefined" + } + }, + { + "desc" : "OS/2", + "ua" : "Mozilla/5.0 (OS/2; Warp 4.5; rv:45.0) Gecko/20100101 Firefox/45.0", + "expect" : + { + "name" : "OS/2", + "version" : "undefined" + } } ] \ No newline at end of file From e1216ff36bc47f2f3664db831af4cf2e054d578a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 15 Apr 2025 21:32:53 +0700 Subject: [PATCH 350/388] Improve OS detection: Arch, Mint --- src/main/ua-parser.js | 8 +-- test/data/ua/os/arch.json | 18 +++++++ test/data/ua/os/debian.json | 9 ++++ test/data/ua/os/kubuntu.json | 9 ++++ test/data/ua/os/mint.json | 99 ++++++++++++++++++++++++++++++++++++ test/data/ua/os/suse.json | 9 ++++ test/data/ua/os/ubuntu.json | 27 ++++++++++ test/data/ua/os/zenwalk.json | 9 ++++ 8 files changed, 184 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b3998746f..f55d0e977 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -976,10 +976,10 @@ // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm - /(mint)[\/\(\) ]?(\w*)/i, // Mint - /(mageia|vectorlinux|fuchsia|arcaos)[; ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS - /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, - // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire + /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint + /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS diff --git a/test/data/ua/os/arch.json b/test/data/ua/os/arch.json index 0aad66706..a186af0f2 100644 --- a/test/data/ua/os/arch.json +++ b/test/data/ua/os/arch.json @@ -1,4 +1,22 @@ [ + { + "desc" : "Arch", + "ua" : "Mozilla/5.0 (X11; Arch Linux i686; rv:2.0) Gecko/20100101 Firefox/126.1", + "expect" : + { + "name" : "Arch", + "version" : "undefined" + } + }, + { + "desc" : "Arch", + "ua" : "Mozilla/5.0 ArchLinux (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1", + "expect" : + { + "name" : "Arch", + "version" : "undefined" + } + }, { "desc" : "Arch", "ua" : "Uzbl (Webkit 1.1.10) (Arch Linux)", diff --git a/test/data/ua/os/debian.json b/test/data/ua/os/debian.json index 4ae949a82..a6a777b2f 100644 --- a/test/data/ua/os/debian.json +++ b/test/data/ua/os/debian.json @@ -52,5 +52,14 @@ "name" : "Debian", "version" : "undefined" } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (X11; U; Linux sparc64; es-PY; rv:5.0) Gecko/20100101 IceCat/5.0 (like Firefox/5.0; Debian-6.0.1)", + "expect" : + { + "name" : "Debian", + "version" : "6.0.1" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/kubuntu.json b/test/data/ua/os/kubuntu.json index 26342568f..d24214236 100644 --- a/test/data/ua/os/kubuntu.json +++ b/test/data/ua/os/kubuntu.json @@ -7,5 +7,14 @@ "name" : "Kubuntu", "version" : "undefined" } + }, + { + "desc" : "Kubuntu", + "ua" : "Mozilla/5.0 (Kubuntu; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0", + "expect" : + { + "name" : "Kubuntu", + "version" : "undefined" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/mint.json b/test/data/ua/os/mint.json index e87641e80..8493d1ac2 100644 --- a/test/data/ua/os/mint.json +++ b/test/data/ua/os/mint.json @@ -25,5 +25,104 @@ "name" : "Mint", "version" : "6" } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Linux Mint/6 (Felicia) Firefox/3.0.4", + "expect" : + { + "name" : "Mint", + "version" : "6" + } + }, + { + "desc" : "Mint", + "ua" : "Opera/9.80 (X11; Linux i686; Edition Linux Mint) Presto/2.12.388 Version/12.15", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Opera/9.51 (X11; Linux i686; U; Linux Mint; en)", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/19.3 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Brave Chrome/83.0.4103.97 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "19.3" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/20 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Kiwi Chrome/100.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "20" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/20.2; x86_64) Gecko/20100101 Firefox/92.0.1", + "expect" : + { + "name" : "Mint", + "version" : "20.2" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.10) Gecko/2009042513 Linux Mint/5 (Elyssa) Firefox/3.0.10", + "expect" : + { + "name" : "Mint", + "version" : "5" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060308 Linux Mint/7 (Gloria) Firefox/54.0", + "expect" : + { + "name" : "Mint", + "version" : "7" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20091020 Linux Mint/8 (Helena) Firefox/3.5.3", + "expect" : + { + "name" : "Mint", + "version" : "8" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:2.0) Gecko/20100101 Linux Mint 16/Petra Firefox/25.0.1.", + "expect" : + { + "name" : "Mint", + "version" : "16" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (Linux x86_64; Mint 21.3) AppleWebKit/537.36 (KHTML, like Gecko) Brave/128.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "21.3" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/suse.json b/test/data/ua/os/suse.json index 9da3f3596..57f5f6989 100644 --- a/test/data/ua/os/suse.json +++ b/test/data/ua/os/suse.json @@ -7,5 +7,14 @@ "name" : "SUSE", "version" : "3.6.17-0.2.1" } + }, + { + "desc" : "SUSE", + "ua" : "Mozilla/5.0 (X11; SUSE; Linux amd64; rv:86.0) Gecko/20100101 Firefox/86.0", + "expect" : + { + "name" : "SUSE", + "version" : "undefined" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/ubuntu.json b/test/data/ua/os/ubuntu.json index d3958f9fc..5438e8934 100644 --- a/test/data/ua/os/ubuntu.json +++ b/test/data/ua/os/ubuntu.json @@ -16,5 +16,32 @@ "name" : "Ubuntu", "version" : "undefined" } + }, + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (Wayland; Linux x86_64; Huawei) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Ubuntu/23.04 Edg/110.0.1587.41", + "expect" : + { + "name" : "Ubuntu", + "version" : "23.04" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (X11; Ubuntu 20.04; Linux arm; rv:99.0) Gecko/20100101 Firefox/99.0", + "expect" : + { + "name" : "Ubuntu", + "version" : "20.04" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16", + "expect" : + { + "name" : "Ubuntu", + "version" : "14.10" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/zenwalk.json b/test/data/ua/os/zenwalk.json index 230b5ddd9..170db50ea 100644 --- a/test/data/ua/os/zenwalk.json +++ b/test/data/ua/os/zenwalk.json @@ -7,5 +7,14 @@ "name" : "Zenwalk", "version" : "7.3" } + }, + { + "desc" : "Zenwalk", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.13) Gecko/20101221 IceCat/3.6.13 (like Firefox/3.6.13) (Zenwalk GNU Linux)", + "expect" : + { + "name" : "Zenwalk", + "version" : "undefined" + } } ] \ No newline at end of file From 848cd1d1e408e4f7f69ecbd89eae3b1e3b906405 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 15 Apr 2025 21:43:59 +0700 Subject: [PATCH 351/388] Add new OS: Knoppix, Xubuntu --- src/enums/ua-parser-enums.d.ts | 2 ++ src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 4 ++-- test/data/ua/os/knoppix.json | 11 +++++++++++ test/data/ua/os/xubuntu.json | 20 ++++++++++++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 test/data/ua/os/knoppix.json create mode 100644 test/data/ua/os/xubuntu.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 8dff5ea96..dc9d4b87c 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -319,6 +319,7 @@ export const OS: Readonly<{ IOS: "iOS"; JOLI: "Joli"; KAIOS: "KaiOS"; + KNOPPIX: "Knoppix"; KUBUNTU: "Kubuntu"; LINPUS: "Linpus"; LINSPIRE: "Linspire"; @@ -371,5 +372,6 @@ export const OS: Readonly<{ WINDOWS_MOBILE: "Windows Mobile"; WINDOWS_PHONE: "Windows Phone"; XBOX: "Xbox"; + XUBUNTU: "Xubuntu"; ZENWALK: "Zenwalk"; }>; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 4bfcec433..ee46d8b09 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -334,6 +334,7 @@ const OS = Object.freeze({ IOS: 'iOS', JOLI: 'Joli', KAIOS: 'KaiOS', + KNOPPIX: 'Knoppix', KUBUNTU: 'Kubuntu', LINPUS: 'Linpus', LINSPIRE: 'Linspire', @@ -386,6 +387,7 @@ const OS = Object.freeze({ WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', XBOX: 'Xbox', + XUBUNTU: 'Xubuntu', ZENWALK: 'Zenwalk' // TODO : test! diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index f55d0e977..88864b7ff 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -978,8 +978,8 @@ /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch - /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, - // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire|knoppix)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire/Knoppix /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS diff --git a/test/data/ua/os/knoppix.json b/test/data/ua/os/knoppix.json new file mode 100644 index 000000000..4ca6cdba1 --- /dev/null +++ b/test/data/ua/os/knoppix.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Knoppix", + "ua" : "Mozilla/5.0 (Knoppix; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.3", + "expect" : + { + "name" : "Knoppix", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/xubuntu.json b/test/data/ua/os/xubuntu.json new file mode 100644 index 000000000..1f38a96ca --- /dev/null +++ b/test/data/ua/os/xubuntu.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Xubuntu", + "ua" : "Mozilla/5.0 (X11; Xubuntu Linux 21.10 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.81 Safari/537.57 Vivaldi/5.1.2 (Blink)", + "expect" : + { + "name" : "Xubuntu", + "version" : "21.10" + } + }, + { + "desc" : "Xubuntu", + "ua" : "Mozilla/5.0 (X11; Xubuntu 14.04.5 LTS) AppleWebKit/537.36 (KHTML, like Gecko) Xubuntu Chrome/66.0.2623.87 Safari/537.36", + "expect" : + { + "name" : "Xubuntu", + "version" : "14.04.5" + } + } +] \ No newline at end of file From e069133f516142ee274f92bb0edfa8c2e05fb64c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 15 Apr 2025 21:49:18 +0700 Subject: [PATCH 352/388] Fix #787 - Avoid using lookbehind assertion in regex to ensure compatibility --- src/extensions/ua-parser-extensions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 6665b6d23..2929f3a86 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -329,7 +329,8 @@ const Libraries = Object.freeze({ // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent [ /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:urllib|requests)|scrapy)\/([\w\.]+)/i, - /(jsdom|(?<=\()java)\/([\w\.]+)/i + /(jsdom)\/([\w\.]+)/i, + /\((java)\/([\w\.]+)/i ], [NAME, VERSION, [TYPE, LIBRARY]] ] }); From b9a710978e88ff1d5480886c2552efaccdad78ae Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 2 May 2025 10:19:19 +0700 Subject: [PATCH 353/388] Fix #791 - Add new device vendor: Vizio --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 1 + test/data/ua/device/vizio.json | 20 ++++++++++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 test/data/ua/device/vizio.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index dc9d4b87c..e5bec20fe 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -255,6 +255,7 @@ export const Vendor: Readonly<{ TESLA: "Tesla"; ULEFONE: "Ulefone"; VIVO: "Vivo"; + VIZIO: "Vizio"; VODAFONE: "Vodafone"; XBOX: "Xbox"; XIAOMI: "Xiaomi"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ee46d8b09..84615b69d 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -266,6 +266,7 @@ const Vendor = Object.freeze({ TESLA: 'Tesla', ULEFONE: 'Ulefone', VIVO: 'Vivo', + VIZIO: 'Vizio', VODAFONE: 'Vodafone', XBOX: 'Xbox', XIAOMI: 'Xiaomi', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 88864b7ff..51c8812be 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -766,6 +766,7 @@ ], [VENDOR, [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(vizio)(?: |.+model\/)(\w+-\w+)/i, // Vizio /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i diff --git a/test/data/ua/device/vizio.json b/test/data/ua/device/vizio.json new file mode 100644 index 000000000..d36ae4d30 --- /dev/null +++ b/test/data/ua/device/vizio.json @@ -0,0 +1,20 @@ +[ + { + "desc": "VIZIO", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36 CrKey/1.0.999999 VIZIO SmartCast(Conjure/SX7B-4.6.419.12 FW/7.0.23.2-4 Model/M557-G0)", + "expect": { + "vendor": "VIZIO", + "model": "M557-G0", + "type": "smarttv" + } + }, + { + "desc": "VIZIO", + "ua": "VIZIO V506-J09 ViziOS/1.4.512.847.1 WatchFree/24.06.13.2 FancyPlayer/1.1.30-qa ", + "expect": { + "vendor": "VIZIO", + "model": "V506-J09", + "type": "smarttv" + } + } +] \ No newline at end of file From 944c1fb26017273b66d65e80e994394b3b0186a5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 10 May 2025 12:28:57 +0700 Subject: [PATCH 354/388] Improve device detection: Motorola --- src/main/ua-parser.js | 17 +- test/data/ua/device/motorola.json | 251 +++++++++++++++++++++++++++--- 2 files changed, 238 insertions(+), 30 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 51c8812be..9152236ce 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -597,10 +597,17 @@ /\b(rmx[1-3]\d{3})(?: bui|;|\))/i ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ + // Lenovo + /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, + /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ + /lenovo[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, MOBILE]], [ + // Motorola /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, - /\bmot(?:orola)?[- ](\w*)/i, - /((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i + /\bmot(?:orola)?[- ]([\w\s]+)(\)| bui)/i, + /((?:moto(?! 360)[-\w\(\) ]+|xt\d{3,4}[cgkosw\+]?[-\d]*|nexus 6)(?= bui|\)))/i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ /\b(mz60\d|xoom[2 ]{0,2}) build\//i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ @@ -613,11 +620,6 @@ /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ - // Lenovo - /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, - /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i - ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ - // Nokia /(nokia) (t[12][01])/i ], [VENDOR, MODEL, [TYPE, TABLET]], [ @@ -734,7 +736,6 @@ /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia - /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ diff --git a/test/data/ua/device/motorola.json b/test/data/ua/device/motorola.json index b9227ba35..18d1e48a3 100644 --- a/test/data/ua/device/motorola.json +++ b/test/data/ua/device/motorola.json @@ -1,37 +1,55 @@ [ { - "desc": "Motorola Moto X", - "ua": "Mozilla/5.0 (Linux; Android 4.4.4; XT1097 Build/KXE21.187-38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", + "desc": "Motorola Defy XT", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; XT555C Build/V1.67D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", "expect": { "vendor": "Motorola", - "model": "XT1097", + "model": "XT555C", "type": "mobile" } }, { - "desc": "Motorola Moto Z3 Play", - "ua": "Mozilla/5.0 (Linux; Android 9; Moto Z3 Play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "desc": "Motorola Droid RAZR 4G", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3; xx-xx; DROID RAZR 4G Build/6.5.1-73_DHD-11_M1-29) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", "expect": { "vendor": "Motorola", - "model": "Moto Z3 Play", + "model": "DROID RAZR 4G", "type": "mobile" } }, { - "desc": "Motorola Nexus 6", - "ua": "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.20 Mobile Safari/537.36", + "desc": "Motorola Edge 30 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; XT2201-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36", "expect": { "vendor": "Motorola", - "model": "Nexus 6", + "model": "XT2201-1", "type": "mobile" } }, { - "desc": "Motorola Droid RAZR 4G", - "ua": "Mozilla/5.0 (Linux; U; Android 2.3; xx-xx; DROID RAZR 4G Build/6.5.1-73_DHD-11_M1-29) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "desc": "Motorola Edge 30 Ultra", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola edge 30 ultra Build/U1SQS34.52-21-1-13; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 M", "expect": { "vendor": "Motorola", - "model": "DROID RAZR 4G", + "model": "edge 30 ultra", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge 40", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola edge 40 Build/U1TL34.115-16-1-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.104 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "edge 40", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge X30", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2201-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.59 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2201-2", "type": "mobile" } }, @@ -45,11 +63,65 @@ } }, { - "desc": "Moto X", - "ua": "Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "desc": "Motorola Moto 360", + "ua": "Mozilla/5.0 (Linux; Android 4.4; Moto 360 Build/KNX01S) AppleWebKit/537.36 (KHTML, like Gecko) WIB/0.9.8 Mobile Safari/537.36", "expect": { "vendor": "Motorola", - "model": "XT1058", + "model": "Moto 360", + "type": "wearable" + } + }, + { + "desc": "Motorola Moto E", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Build/NDQS26.69-64-11-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto E (4)", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E6s", + "ua": "Mozilla/5.0 (Linux; Android 9; MOTOROLA E6S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "E6S", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E7 Plus", + "ua": "Mozilla/5.0 (Linux; Android 10; moto e(7) plus Build/QPZS30.30-Q3-38-69-12; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.14 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "moto e(7) plus", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E7 Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2081-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Mobile Safari/537.36 OPR/102.0.4864.0", + "expect": { + "vendor": "Motorola", + "model": "XT2081-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; moto g 5G - 2023 Build/U1TPNS34.26-48-2-7; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Motorola", + "model": "moto g 5G - 2023", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G 5G (2022)", + "ua": "Mozilla/5.0 (Linux; Android 12; moto g 5G (2022) Build/S1SAS32.47-77-9; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.116 Mobile Safari/537.36 [FB_IAB/Orca-Android;FBAV/402.0.0.11.101;]", + "expect": { + "vendor": "Motorola", + "model": "moto g 5G (2022)", "type": "mobile" } }, @@ -72,21 +144,156 @@ } }, { - "desc": "Motorola Moto E", - "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Build/NDQS26.69-64-11-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "desc": "Motorola Moto G10", + "ua": "Mozilla/5.0 (Linux; Android 12; XT2127-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36", "expect": { "vendor": "Motorola", - "model": "Moto E (4)", + "model": "XT2127-2", "type": "mobile" } }, { - "desc": "Motorola Moto 360", - "ua": "Mozilla/5.0 (Linux; Android 4.4; Moto 360 Build/KNX01S) AppleWebKit/537.36 (KHTML, like Gecko) WIB/0.9.8 Mobile Safari/537.36", + "desc": "Motorola Moto G24 Power", + "ua": "Mozilla/5.0 (Linux; U; Android 14; moto g24 power Build/UTAS34.82-97-1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.138 Mobile Safari/537.36 OPR/89.0.2254.76420", "expect": { "vendor": "Motorola", - "model": "Moto 360", - "type": "wearable" + "model": "moto g24 power", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G30", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2129-3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2129-3", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G54 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; moto g54 5G Build/U1TDS34.94-12-9-10-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.122 Mobile Safari/537.36 OPX/2", + "expect": { + "vendor": "Motorola", + "model": "moto g54 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; XT1097 Build/KXE21.187-38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT1097", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Motorola", + "model": "XT1058", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X Force", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; XT1580 Build/NPKS25.200-12-9; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Mobile Safari/537.36 OPR/32.0.2254.123747", + "expect": { + "vendor": "Motorola", + "model": "XT1580", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto S30", + "ua": "Mozilla/5.0 (Linux; Android 13; XT2243-2 Build/T1SJC33.51-19-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36 T7/13.30 SP-engine/2.53.0 matrixstyle/0 lite baiduboxapp/6.2.0.10 (Baidu; P1 13) NABar/1.0", + "expect": { + "vendor": "Motorola", + "model": "XT2243-2", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X30 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; XT2241-1 Build/T1SQ33.111-12-19) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.1.3.1264 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2241-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto Z3 Play", + "ua": "Mozilla/5.0 (Linux; Android 9; Moto Z3 Play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto Z3 Play", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto Z4", + "ua": "Mozilla/5.0 (Linux; U; Android 10; moto z4 Build/QDF30.130-42-5-17; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 OPR/2.3.0", + "expect": { + "vendor": "Motorola", + "model": "moto z4", + "type": "mobile" + } + }, + { + "desc": "Motorola Nexus 6", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.20 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Nexus 6", + "type": "mobile" + } + }, + { + "desc": "Motorola One 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; motorola one 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "one 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola P30", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; XT1943-1 Build/OPM1.171019.019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36 T7/13.50 SP-engine/2.88.0 baiduboxapp/13.50.5.10 (Baidu; P1 8.1.0) NABar/1.0", + "expect": { + "vendor": "Motorola", + "model": "XT1943-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; motorola razr 5G Build/S2PS32.57-23-21; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;IABMV/1;]", + "expect": { + "vendor": "Motorola", + "model": "razr 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; XT2071-4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2071-4", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 50 Ultra", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola razr 50 ultra Build/U3UX34.56-29-2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.260 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "razr 50 ultra", + "type": "mobile" } } ] \ No newline at end of file From 14a51039aa4fb67fbfadf4eb44a10b84e9c0c77d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 10 May 2025 19:12:26 +0700 Subject: [PATCH 355/388] Improve device detection: identify `Large Screen` as `smarttv` --- src/main/ua-parser.js | 2 +- test/data/ua/device/_others.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 9152236ce..a5ab0860b 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -803,7 +803,7 @@ // SmartTV from Unidentified Vendors /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i ], [MODEL, [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:|large screen[\w ]+safari)\b/i ], [[TYPE, SMARTTV]], [ /////////////////// diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index c47da246c..b545f3959 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -281,6 +281,24 @@ "type": "smarttv" } }, + { + "desc": "Unknown TV", + "ua": "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.127 Large Screen Safari/533.4 GoogleTV/ 162671", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Unknown TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen WebAppManager Safari/537.41", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, { "desc": "PDA with Windows CE", "ua": "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", From 6a2a3a61b63c52887f18655ec4f3cf99cdcdfe0c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 12 May 2025 11:37:44 +0700 Subject: [PATCH 356/388] Fix mistakenly placed enum value --- src/enums/ua-parser-enums.d.ts | 2 +- src/enums/ua-parser-enums.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index e5bec20fe..7552764d4 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -16,7 +16,6 @@ export const Browser: Readonly<{ BAIDU: "Baidu Browser"; BASILISK: "Basilisk"; BLAZER: "Blazer"; - BLU: "BLU"; BOLT: "Bolt"; BOWSER: "Bowser"; BRAVE: "Brave"; @@ -203,6 +202,7 @@ export const Vendor: Readonly<{ ATT: "AT&T"; BENQ: "BenQ"; BLACKBERRY: "BlackBerry"; + BLU: "BLU"; CAT: "Cat"; DELL: "Dell"; ENERGIZER: "Energizer"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 84615b69d..4a784b4f0 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -21,7 +21,6 @@ const Browser = Object.freeze({ BAIDU: 'Baidu Browser', BASILISK: 'Basilisk', BLAZER: 'Blazer', - BLU: 'BLU', BOLT: 'Bolt', BOWSER: 'Bowser', BRAVE: 'Brave', @@ -214,6 +213,7 @@ const Vendor = Object.freeze({ ATT: 'AT&T', BENQ: 'BenQ', BLACKBERRY: 'BlackBerry', + BLU: 'BLU', CAT: 'Cat', DELL: 'Dell', ENERGIZER: 'Energizer', From be16c04a33c8c0833b08e43a3c784e4701f2dc66 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 12 May 2025 14:00:58 +0700 Subject: [PATCH 357/388] Improve device detection: Nothing, Sony --- src/main/ua-parser.js | 4 +-- test/data/ua/device/nothing.json | 36 +++++++++++++++++++++++++ test/data/ua/device/sony.json | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index a5ab0860b..09f280636 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -634,7 +634,7 @@ ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ // Sony - /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i + /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [ /sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i @@ -715,7 +715,7 @@ ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [ // Nothing - /droid.+; (a(?:015|06[35]|142p?))/i + /droid.+; (a(in)?(0(15|59|6[35])|142)p?)/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // Archos diff --git a/test/data/ua/device/nothing.json b/test/data/ua/device/nothing.json index 56dc460e3..12b418d2c 100644 --- a/test/data/ua/device/nothing.json +++ b/test/data/ua/device/nothing.json @@ -17,6 +17,15 @@ "type": "mobile" } }, + { + "desc": "Nothing 2", + "ua": "Mozilla/5.0 (Linux; Android 15; AIN065 Build/AQ3A.240929.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.90 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/496.0.0.45.65;IABMV/1;] FBNV/500", + "expect": { + "vendor": "Nothing", + "model": "AIN065", + "type": "mobile" + } + }, { "desc": "Nothing 2a", "ua": "Mozilla/5.0 (Linux; Android 14; A142 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.71 Mobile Safari/537.36", @@ -25,5 +34,32 @@ "model": "A142", "type": "mobile" } + }, + { + "desc": "Nothing 2a Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; A142P Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.107 Mobile Safari/537.36 OPX/2.6", + "expect": { + "vendor": "Nothing", + "model": "A142P", + "type": "mobile" + } + }, + { + "desc": "Nothing 3a", + "ua": "Mozilla/5.0 (Linux; Android 15; A059 Build/AQ3A.241015.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A059", + "type": "mobile" + } + }, + { + "desc": "Nothing 3a Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; A059P Build/AQ3A.241015.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/136.0.7103.57 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/511.0.0.73.36;IABMV/1;]", + "expect": { + "vendor": "Nothing", + "model": "A059P", + "type": "mobile" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/sony.json b/test/data/ua/device/sony.json index 92151af84..d4a3c21cb 100644 --- a/test/data/ua/device/sony.json +++ b/test/data/ua/device/sony.json @@ -98,6 +98,15 @@ "type": "mobile" } }, + { + "desc": "Sony Xperia 1 VI", + "ua": "Mozilla/5.0 (Linux; Android 14; XQ-EC44) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-EC44", + "type": "mobile" + } + }, { "desc": "Sony Xperia 10ii", "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AU52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", @@ -107,6 +116,24 @@ "type": "mobile" } }, + { + "desc": "Sony Xperia 10 III Lite", + "ua": "Mozilla/5.0 (Linux; Android 12; XQ-BT44 Build/62.1.A.1.227; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 Sleipnir/3.6.0", + "expect": { + "vendor": "Sony", + "model": "XQ-BT44", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 10 VI", + "ua": "Mozilla/5.0 (Linux; Android 14; XQ-ES72 Build/70.0.A.2.233; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.71 Mobile Safari/537.36 Line/14.16.0/IAB", + "expect": { + "vendor": "Sony", + "model": "XQ-ES72", + "type": "mobile" + } + }, { "desc": "Sony Xperia Pro", "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AQ52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.185 Mobile Safari/537.36", @@ -116,6 +143,24 @@ "type": "mobile" } }, + { + "desc": "Sony Xperia Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; Sony XQ-AQ52) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AQ52", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia Pro I", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; XQ-BE72) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.148 YaBrowser/22.7.3.82.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-BE72", + "type": "mobile" + } + }, { "desc": "Sony SGP521 (Xperia Z2 Tablet)", "ua": "Mozilla/5.0 (Linux; Android 4.4; SGP521 Build/17.1.A.0.432) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", From 3d6e326f49602bfb39bbb034e87f7dbb53e3b38a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 12 May 2025 15:46:37 +0700 Subject: [PATCH 358/388] Improve device detection: Google Pixel & Pixelbook series --- src/main/ua-parser.js | 5 +- test/data/ua/device/google.json | 448 +++++++++++++++++++++++++++++--- 2 files changed, 417 insertions(+), 36 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 09f280636..d6b7e04dd 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -630,8 +630,11 @@ // Google /(pixel (c|tablet))\b/i // Google Pixel C/Tablet ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ - /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel + // Google Pixel + /droid.+;(?: google)? (g(01[13]a|020[aem]|025[jn]|1b60|1f8f|2ybb|4s1m|576d|5nz6|8hhn|8vou|a02099|c15s|d1yq|e2ae|ec77|gh2x|kv4x|p4bc|pj41|r83y|tt9q|ur25|wvk6)|pixel[\d ]*a?( pro)?( xl)?( fold)?( \(5g\))?)( bui|\))/i ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ + /(google) (pixelbook( go)?)/i + ], [VENDOR, MODEL], [ // Sony /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i diff --git a/test/data/ua/device/google.json b/test/data/ua/device/google.json index a06b99127..0897534e7 100644 --- a/test/data/ua/device/google.json +++ b/test/data/ua/device/google.json @@ -72,56 +72,29 @@ } }, { - "desc": "Google Pixel Tablet", - "ua": "Mozilla/5.0 (Linux; Android 14; Pixel Tablet Build/AP2A.240905.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", - "expect": { - "vendor": "Google", - "model": "Pixel Tablet", - "type": "tablet" - } - }, - { - "desc": "Google Pixel Watch", - "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch Build/TWD4.231005.002)", - "expect": { - "vendor": "Google", - "model": "Pixel Watch", - "type": "wearable" - } - }, - { - "desc": "Google Pixel Watch 2", - "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch 2 Build/TWD9.240605.001.A1)", - "expect": { - "vendor": "Google", - "model": "Pixel Watch 2", - "type": "wearable" - } - }, - { - "desc": "Google Pixel XL", - "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", + "desc": "Google Pixel 2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", "expect": { "vendor": "Google", - "model": "Pixel XL", + "model": "Pixel 2", "type": "mobile" } }, { - "desc": "Google Pixel XL", - "ua": "Mozilla/5.0 (Linux; Android 9; Pixel XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "desc": "Google Pixel 2", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 2) AppleWebKit/537.36 (KHTML, like Gecko) Edg/57.0.986.6", "expect": { "vendor": "Google", - "model": "Pixel XL", + "model": "Pixel 2", "type": "mobile" } }, { "desc": "Google Pixel 2", - "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; G011A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Safari/537.36 OPR/68.3.3557.64528", "expect": { "vendor": "Google", - "model": "Pixel 2", + "model": "G011A", "type": "mobile" } }, @@ -152,6 +125,24 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 3", + "ua": "Mozilla/5.0 (Linux; Android 11; G013A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G013A", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3a", + "ua": "Mozilla/5.0 (Linux; Android 14; G020E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G020E", + "type": "mobile" + } + }, { "desc": "Google Pixel 3 XL", "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", @@ -197,6 +188,15 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 4", + "ua": "Mozilla/5.0 (Linux; Android 12; G020M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G020M", + "type": "mobile" + } + }, { "desc": "Google Pixel 4a", "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.83 Mobile Safari/537.36", @@ -206,6 +206,42 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 13; G025J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G025J", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 12; GA02099) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GA02099", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 13; G025N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G025N", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a (5G)", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 4a (5G) Build/UP1A.231105.001.B2; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Google", + "model": "Pixel 4a (5G)", + "type": "mobile" + } + }, { "desc": "Google Pixel 4 XL", "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", @@ -215,6 +251,33 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 13; G5NZ6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G5NZ6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 12; GD1YQ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GD1YQ", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 12; GTT9Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GTT9Q", + "type": "mobile" + } + }, { "desc": "Google Pixel 5", "ua": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.120 Mobile Safari/537.36", @@ -224,6 +287,51 @@ "type": "mobile" } }, + { + "desc": "Google Pixel 5a 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; G4S1M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G4S1M", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5a 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; G1F8F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G1F8F", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6", + "ua": "Mozilla/5.0 (Linux; Android 11; Pixel 6 Build/QP1A.190711.020) AppleWebKit/545.31 (KHTML, like Gecko) Firefox/109.0.2318.118 Mobile Safari/545.22", + "expect": { + "vendor": "Google", + "model": "Pixel 6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel 6 Pro Build/AP4A.241205.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36 Line/15.0.0/IAB", + "expect": { + "vendor": "Google", + "model": "Pixel 6 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; G8VOU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G8VOU", + "type": "mobile" + } + }, { "desc": "Google Pixel 7", "ua": "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", @@ -232,5 +340,275 @@ "model": "Pixel 7", "type": "mobile" } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel 7 Pro Build/AP4A.250205.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.121 Mobile Safari/537.36 musical_ly_2023808030 BytedanceWebview/d8a21c6", + "expect": { + "vendor": "Google", + "model": "Pixel 7 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; GP4BC) AppleWebKit/537.46 (KHTML, like Gecko) Chrome/118.0.5993.80 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GP4BC", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; GE2AE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.48 Mobile Safari/537.46", + "expect": { + "vendor": "Google", + "model": "GE2AE", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7a", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 7a Build/AP2A.240905.003; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Google", + "model": "Pixel 7a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Build/UPB2.230407.014) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.2.7822.95 Mobile Safari/537.36 Vivaldi/5.2.9076.131", + "expect": { + "vendor": "Google", + "model": "Pixel 8", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro Build/UPB1.230309.017; Rooted) AppleWebKit/537.36 (KHTML, like Gecko) Puffin/10.9.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 8 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 14; Google Pixel 8a) Chrome/121.0.6167 Mobile", + "expect": { + "vendor": "Google", + "model": "Pixel 8a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; GKV4X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GKV4X", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; G8HHN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G8HHN", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; G576D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G576D", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; U; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.119 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 9", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; G2YBB) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G2YBB", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; GUR25) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GUR25", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; GWVK6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GWVK6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; G1B60) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G1B60", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro Build/AD1A.240530.047; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.170 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/506.1.0.74.27;IABMV/1;]", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; GR83Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GR83Y", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; GEC77) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GEC77", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro Fold", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; GC15S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GC15S", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; GGH2X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GGH2X", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro XL", + "ua": "Mozilla/5.0 (Linux; U; Android 15; Pixel 9 Pro XL Build/AP4A.250105.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.89 Mobile Safari/537.36 OPR/88.0.2254.75874", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel Fold Build/AP4A.250105.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.40.36;IABMV/1;]", + "expect": { + "vendor": "Google", + "model": "Pixel Fold", + "type": "mobile" + } + }, + { + "desc": "Google Pixel Tablet", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel Tablet Build/AP2A.240905.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel Tablet", + "type": "tablet" + } + }, + { + "desc": "Google Pixel Watch", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch Build/TWD4.231005.002)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch", + "type": "wearable" + } + }, + { + "desc": "Google Pixel Watch 2", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch 2 Build/TWD9.240605.001.A1)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch 2", + "type": "wearable" + } + }, + { + "desc": "Google Pixel XL", + "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixelbook", + "ua": "Mozilla/5.0 (Linux; Android 9; Google Pixelbook) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixelbook", + "type": "undefined" + } + }, + { + "desc": "Google Pixelbook Go", + "ua": "Mozilla/5.0 (Linux; Android 9; Google Pixelbook Go) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 OPR/62.3.3146.57763", + "expect": { + "vendor": "Google", + "model": "Pixelbook Go", + "type": "undefined" + } } ] \ No newline at end of file From 466a8b26be6e931aafd4eb981607ce9b28fca5f4 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 12 May 2025 21:10:55 +0700 Subject: [PATCH 359/388] Improve device detection: OnePlus --- src/main/ua-parser.js | 14 +- test/data/ua/device/_others.json | 9 -- test/data/ua/device/oneplus.json | 228 ++++++++++++++++++++++++++++++- test/data/ua/device/oppo.json | 9 -- test/unit/ua-ch.js | 2 +- 5 files changed, 230 insertions(+), 32 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d6b7e04dd..d1624daf4 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -578,12 +578,17 @@ / ([\w ]+) miui\/v?\d/i ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + // OnePlus + /droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i, + /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ + // OPPO /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i ], [MODEL, [VENDOR, OPPO], [TYPE, MOBILE]], [ /\b(opd2(\d{3}a?))(?: bui|\))/i - ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [ // BLU Vivo Series /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i @@ -643,11 +648,6 @@ /\b(?:sony)?sgp\w+(?: bui|\))/i ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [ - // OnePlus - / (kb2005|in20[12]5|be20[12][59])\b/i, - /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i - ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ - // Amazon /(alexa)webm/i, /(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show @@ -736,7 +736,7 @@ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL + /; (blu|hmd|imo|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(oppo) ?([\w ]+) bui/i // OPPO diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index b545f3959..c58a01e95 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -122,15 +122,6 @@ "type": "undefined" } }, - { - "desc": "OnePlus 7T Pro", - "ua": "Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36 EdgA/110.0.1587.66", - "expect": { - "vendor": "undefined", - "model": "HD1913", - "type": "mobile" - } - }, { "desc": "Philips SmartTV", "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", diff --git a/test/data/ua/device/oneplus.json b/test/data/ua/device/oneplus.json index 74ddcf6ca..a02c0f3c9 100644 --- a/test/data/ua/device/oneplus.json +++ b/test/data/ua/device/oneplus.json @@ -54,11 +54,20 @@ } }, { - "desc": "OnePlus 8T", - "ua": "Mozilla/5.0 (Linux; Android 11; KB2005) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "desc": "OnePlus 7T Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36 EdgA/110.0.1587.66", "expect": { "vendor": "OnePlus", - "model": "KB2005", + "model": "HD1913", + "type": "mobile" + } + }, + { + "desc": "OnePlus 8 Lite", + "ua": "Mozilla/5.0 (Linux; Android 10; OnePlus 8 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4741.47 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "8 Lite", "type": "mobile" } }, @@ -72,11 +81,191 @@ } }, { - "desc": "OnePlus Nord N100", - "ua": "Mozilla/5.0 (Linux; Android 10; BE2015 Build/QKQ1.200719.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36", + "desc": "OnePlus 8T", + "ua": "Mozilla/5.0 (Linux; Android 11; KB2005) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", "expect": { "vendor": "OnePlus", - "model": "BE2015", + "model": "KB2005", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9", + "ua": "Mozilla/5.0 (Android 14; SDK 28; OnePlus 9) Gecko/128.0 Firefox/128.0", + "expect": { + "vendor": "OnePlus", + "model": "9", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9", + "ua": "Mozilla/5.0 (Linux; Android 13; LE2113 Build/TP1A.220905.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 Flipboard/4.3.14/5377,4.3.14.5377", + "expect": { + "vendor": "OnePlus", + "model": "LE2113", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; LE2123) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Mobile Safari/537.36 OPR/73.0.3832.69371", + "expect": { + "vendor": "OnePlus", + "model": "LE2123", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9R", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; LE2100) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 YaApp_Android/21.114.1 YaSearchBrowser/21.114.1 BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "LE2100", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9RT 5G", + "ua": "Mozilla/5.0 (Linux; U; Android 11; en-in; MT2111 Build/RKQ1.210614.002) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 GSA/13.14.15.23.arm64", + "expect": { + "vendor": "OnePlus", + "model": "MT2111", + "type": "mobile" + } + }, + { + "desc": "OnePlus 10 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; NE2213) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.148 YaBrowser/22.7.5.90.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "NE2213", + "type": "mobile" + } + }, + { + "desc": "OnePlus 10RT", + "ua": "Mozilla/5.0 (Linux; Android 13; CPH2413) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "CPH2413", + "type": "mobile" + } + }, + { + "desc": "OnePlus 11R", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2487) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "CPH2487", + "type": "mobile" + } + }, + { + "desc": "OnePlus 13", + "ua": "Mozilla/5.0 (Linux; Android 15; CPH2653 Build/AP3A.240617.008; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.92 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/504.0.0.69.64;IABMV/1;] FBNV/5", + "expect": { + "vendor": "OnePlus", + "model": "CPH2653", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PGKM10 Build/AP3A.240617.008) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.168 Mobile Safari/537.36 HeyTapBrowser/40.9.6.2", + "expect": { + "vendor": "OnePlus", + "model": "PGKM10", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-CN; PHK110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.3.6.1367 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PHK110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2 Pro", + "ua": "Mozilla/5.0 (Linux; arm; Android 13; PJA110) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 YaBrowser/23.1.0.284.00 (beta) SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJA110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2V", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PHP110 Build/AP3A.240617.008) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/121.0.6167.71 MQQBrowser/16.3 Mobile Safari/537.36 COVC/047707", + "expect": { + "vendor": "OnePlus", + "model": "PHP110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-CN; PJE110 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.80 Quark/7.9.7.782 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJE110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3 Pro 5G", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-CN; PJX110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.80 Quark/7.11.4.814 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJX110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3V", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-cn; PJF110 Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.61 Mobile Safari/537.36 HeyTapBrowser/40.8.33.1.2beta", + "expect": { + "vendor": "OnePlus", + "model": "PJF110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 5", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PKG110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.168 Mobile Safari/537.36 HeyTapBrowser/40.9.9.2", + "expect": { + "vendor": "OnePlus", + "model": "PKG110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-CN; PGP110 Build/UKQ1.230924.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.2.9.1360 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PGP110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace Racing Edition", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; PGZ110 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.3.6.1367 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PGZ110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord CE 4", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2613) AppleWebKit/537.36 (KHTML, like Gecko) JioSphere/5.0.4 Chrome/119.0.6045.193 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "CPH2613", "type": "mobile" } }, @@ -89,6 +278,33 @@ "type": "mobile" } }, + { + "desc": "OnePlus Nord N30 SE 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2605 Build/TP1A.220905.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/135.0.7049.92 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/509.0.0.67.28;IABMV/1;]", + "expect": { + "vendor": "OnePlus", + "model": "CPH2605", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord N100", + "ua": "Mozilla/5.0 (Linux; Android 10; BE2015 Build/QKQ1.200719.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "BE2015", + "type": "mobile" + } + }, + { + "desc": "OnePlus Open", + "ua": "Dalvik/2.1.0 (Linux; U; Android 15; CPH2551 Build/AP3A.240617.008)", + "expect": { + "vendor": "OnePlus", + "model": "CPH2551", + "type": "mobile" + } + }, { "desc": "OnePlus Pad Go 11.35", "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; OPD2304) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.629 YaApp_Android/24.101/apad YaSearchBrowser/24.101/apad BroPP/1.0 SA/3 Mobile Safari/537.36", diff --git a/test/data/ua/device/oppo.json b/test/data/ua/device/oppo.json index 367039e05..ab63e2609 100644 --- a/test/data/ua/device/oppo.json +++ b/test/data/ua/device/oppo.json @@ -1,13 +1,4 @@ [ - { - "desc": "OnePlus 10RT", - "ua": "Mozilla/5.0 (Linux; Android 13; CPH2413) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", - "expect": { - "vendor": "OPPO", - "model": "CPH2413", - "type": "mobile" - } - }, { "desc": "OPPO Pad", "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; OPD2101 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36", diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 54e2f9a7c..18dd09a4e 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -470,7 +470,7 @@ describe('Identify vendor & type of device from given model name', () => { { model: 'CPH2389', expect: { - vendor : 'OPPO', + vendor : 'OnePlus', type : 'mobile' } }, From 72c59b5351ed595eb0ffed4fb2911400078851c9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 13 May 2025 11:18:41 +0700 Subject: [PATCH 360/388] Add new device vendor: Lava https://www.lavamobiles.com/ --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 2 +- test/data/ua/device/lava.json | 110 +++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 test/data/ua/device/lava.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 7552764d4..b41dfc460 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -221,6 +221,7 @@ export const Vendor: Readonly<{ ITEL: "itel"; JOLLA: "Jolla"; KOBO: "Kobo"; + LAVA: "Lava"; LENOVO: "Lenovo"; LG: "LG"; MEIZU: "Meizu"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 4a784b4f0..2ae437cc7 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -232,6 +232,7 @@ const Vendor = Object.freeze({ ITEL: 'itel', JOLLA: 'Jolla', KOBO: 'Kobo', + LAVA: 'Lava', LENOVO: 'Lenovo', LG: 'LG', MEIZU: 'Meizu', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index d1624daf4..9d9279d33 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -736,7 +736,7 @@ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (blu|hmd|imo|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/OnePlus/TCL + /; (blu|hmd|imo|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Lava/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(oppo) ?([\w ]+) bui/i // OPPO diff --git a/test/data/ua/device/lava.json b/test/data/ua/device/lava.json new file mode 100644 index 000000000..5279f2cf5 --- /dev/null +++ b/test/data/ua/device/lava.json @@ -0,0 +1,110 @@ +[ + { + "desc": "Lava Agni 2 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; LAVA LXX504 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.46 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/499.0.0.31.60;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LXX504", + "type": "mobile" + } + }, + { + "desc": "Lava Agni 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; LAVA LXX501 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.119 Mobile Safari/537.36[FBAN/EMA;FBLC/hi_IN;FBAV/396.0.0.9.115;]", + "expect": { + "vendor": "LAVA", + "model": "LXX501", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze", + "ua": "Mozilla/5.0 (Linux; U; Android 12; en-US; LAVA Blaze Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.4.0.1306 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "Blaze", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze 2", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LZX409 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LZX409", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; LAVA LXX503) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LXX503", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze Curve", + "ua": "Mozilla/5.0 (Linux; Android 14; LAVA LXX505 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/505.0.0.57.45;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LXX505", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LZX404 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.50 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/503.0.0.69.76;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LZX404", + "type": "mobile" + } + }, + { + "desc": "Lava Iris 46", + "ua": "Mozilla/5.0 (Linux; Android 9; LAVA LH9950) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/21.0 Chrome/110.0.5481.154 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LH9950", + "type": "mobile" + } + }, + { + "desc": "Lava Iris 54", + "ua": "Mozilla/5.0 (Linux; U; Android 9; LAVA LH9931 Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 OPR/39.1.2254.136708", + "expect": { + "vendor": "LAVA", + "model": "LH9931", + "type": "mobile" + } + }, + { + "desc": "Lava Storm 5G", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LXX508 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.100 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LXX508", + "type": "mobile" + } + }, + { + "desc": "Lava V7 Prime", + "ua": "Mozilla/5.0 (Linux; Android 9; LAVA LE9940) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LE9940", + "type": "mobile" + } + }, + { + "desc": "Lava V7s Prime", + "ua": "Mozilla/5.0 (Linux; U; Android 9; LAVA LE9940_W Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 OPR/43.2.2254.140294", + "expect": { + "vendor": "LAVA", + "model": "LE9940_W", + "type": "mobile" + } + } +] \ No newline at end of file From f51ed7911856fe87786503a22b4cec42fbd7f5f8 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 20 May 2025 16:41:00 +0700 Subject: [PATCH 361/388] Improve device detection: HMD, Infinix, Tecno --- src/main/ua-parser.js | 12 ++++++++---- test/data/ua/device/hmd.json | 27 +++++++++++++++++++++++++++ test/data/ua/device/infinix.json | 27 +++++++++++++++++++++++++++ test/data/ua/device/tecno.json | 27 +++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 9d9279d33..ae91dbd09 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -729,14 +729,18 @@ /; (ac[3-6]\d\w{2,8})( b|\))/i ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [ + // HMD + /; (n159v)/i + ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [ + // MIXED /(imo) (tab \w+)/i, // IMO - /(infinix) (x1101b?)/i // Infinix XPad + /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, - // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (blu|hmd|imo|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Lava/OnePlus/TCL + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan + /; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(oppo) ?([\w ]+) bui/i // OPPO diff --git a/test/data/ua/device/hmd.json b/test/data/ua/device/hmd.json index a7d38e2f3..037148c41 100644 --- a/test/data/ua/device/hmd.json +++ b/test/data/ua/device/hmd.json @@ -8,6 +8,15 @@ "type": "mobile" } }, + { + "desc": "HMD Fusion", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Fusion) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Fusion", + "type": "mobile" + } + }, { "desc": "HMD Pulse", "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", @@ -34,5 +43,23 @@ "model": "Pulse Pro", "type": "mobile" } + }, + { + "desc": "HMD Skyline", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; HMD Skyline) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.1804 YaApp_Android/24.120.1 YaSearchBrowser/24.120.1 BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Skyline", + "type": "mobile" + } + }, + { + "desc": "HMD Vibe", + "ua": "Mozilla/5.0 (Linux; Android 14; N159V Build/UKQ1.231025.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/135.0.7049.111 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;]", + "expect": { + "vendor": "HMD", + "model": "N159V", + "type": "mobile" + } } ] \ No newline at end of file diff --git a/test/data/ua/device/infinix.json b/test/data/ua/device/infinix.json index 6c356d3c2..3474003b9 100644 --- a/test/data/ua/device/infinix.json +++ b/test/data/ua/device/infinix.json @@ -1,4 +1,22 @@ [ + { + "desc": "Infinix Hot 4", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; en-us; Infinix HOT 4 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36 PHX/17.9", + "expect": { + "vendor": "Infinix", + "model": "HOT 4", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 4 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Infinix_X556_LTE Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/390.0.0.27.105;]", + "expect": { + "vendor": "Infinix", + "model": "X556_LTE", + "type": "mobile" + } + }, { "desc": "Infinix Hot 7 Pro", "ua": "Mozilla/5.0 (Linux; Android 9; Infinix X625C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", @@ -26,6 +44,15 @@ "type": "mobile" } }, + { + "desc": "Infinix Note 3", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Infinix_X601_LTE Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/81.0.4044.111 Mobile Safari/537.36 GSA/11.5.9.21.arm64", + "expect": { + "vendor": "Infinix", + "model": "X601_LTE", + "type": "mobile" + } + }, { "desc": "Infinix Smart 5", "ua": "Mozilla/5.0 (Linux; Android 10; Infinix X657C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", diff --git a/test/data/ua/device/tecno.json b/test/data/ua/device/tecno.json index a002fcf69..4413f9531 100644 --- a/test/data/ua/device/tecno.json +++ b/test/data/ua/device/tecno.json @@ -1,4 +1,31 @@ [ + { + "desc": "Tecno DroiPad 8D", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1; TECNO DP8D Build/LMY47D; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36 OPR/42.0.2254.139280", + "expect": { + "vendor": "TECNO", + "model": "DP8D", + "type": "tablet" + } + }, + { + "desc": "Tecno DroiPad 10A Pro", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TECNO DP10A Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.74 Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "DP10A Pro", + "type": "tablet" + } + }, + { + "desc": "Tecno DroiPad 10D 4G", + "ua": "Mozilla/5.0 (Linux; Android 7.0; TECNO P904) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36 OPR/77.0.4054.90", + "expect": { + "vendor": "TECNO", + "model": "P904", + "type": "tablet" + } + }, { "desc": "Tecno KC8", "ua": "Mozilla/5.0 (Linux; Android 10; TECNO KC8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", From 64a933bae8cc5f9bc2638300ed608faab79fd7bd Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 20 May 2025 16:45:47 +0700 Subject: [PATCH 362/388] [extensions] Add new fetcher: `MistralAI-User`, `Perplexity-User` --- src/extensions/ua-parser-extensions.js | 4 +++- test/data/ua/extension/fetcher.json | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 2929f3a86..8317f4b21 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -222,8 +222,10 @@ const Fetchers = Object.freeze({ // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan + // Perplexity-User - https://docs.perplexity.ai/guides/bots + // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|(?:bing|microsoft)preview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index fed25bc4f..62154a995 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -149,6 +149,26 @@ "type" : "fetcher" } }, + { + "desc" : "MistralAI-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; MistralAI-User/1.0; +https://docs.mistral.ai/robots)", + "expect" : + { + "name" : "MistralAI-User", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Perplexity-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Perplexity-User/1.0; +https://perplexity.ai/perplexity-user)", + "expect" : + { + "name" : "Perplexity-User", + "version" : "1.0", + "type" : "fetcher" + } + }, { "desc" : "Pinterestbot", "ua" : "Mozilla/5.0 (compatible; Pinterestbot/1.0; +http://www.pinterest.com/bot.html)", From 29677bcd3d2c0436734b8f54fe6283c054bfd45d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 21 May 2025 09:58:01 +0700 Subject: [PATCH 363/388] Add new browser: `Edge WebView`, `Edge WebView2` --- src/enums/ua-parser-enums.d.ts | 2 ++ src/enums/ua-parser-enums.js | 2 ++ src/main/ua-parser.js | 19 +++++++++++++++---- test/data/ua/browser/browser-all.json | 20 ++++++++++++++++++++ test/unit/ua-ch.js | 13 +++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index b41dfc460..44fefbf96 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -38,6 +38,8 @@ export const Browser: Readonly<{ DUCKDUCKGO: "DuckDuckGo"; ECOSIA: "Ecosia"; EDGE: "Edge"; + EDGE_WEBVIEW: "Edge WebView"; + EDGE_WEBVIEW2: "Edge WebView2"; EPIPHANY: "Epiphany"; FACEBOOK: "Facebook"; FALKON: "Falkon"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 2ae437cc7..3420acba4 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -43,6 +43,8 @@ const Browser = Object.freeze({ DUCKDUCKGO: 'DuckDuckGo', ECOSIA: 'Ecosia', EDGE: 'Edge', + EDGE_WEBVIEW: 'Edge WebView', + EDGE_WEBVIEW2: 'Edge WebView2', EPIPHANY: 'Epiphany', FACEBOOK: 'Facebook', FALKON: 'Falkon', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index ae91dbd09..1e6210c4a 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -152,11 +152,11 @@ has = function (str1, str2) { if (typeof str1 === OBJ_TYPE && str1.length > 0) { for (var i in str1) { - if (lowerize(str1[i]) == lowerize(str2)) return true; + if (lowerize(str2) == lowerize(str1[i])) return true; } return false; } - return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; + return isString(str1) ? lowerize(str2) == lowerize(str1) : false; }, isExtensions = function (obj, deep) { for (var prop in obj) { @@ -326,7 +326,9 @@ // Most common regardless engine /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ - /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge + /webview.+edge\/([\w\.]+)/i // Microsoft Edge + ], [VERSION, [NAME, EDGE+' WebView']], [ + /edg(?:e|ios|a)?\/([\w\.]+)/i ], [VERSION, [NAME, 'Edge']], [ // Presto based @@ -441,6 +443,9 @@ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ + /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2 + ], [VERSION, [NAME, EDGE+' WebView2']], [ + / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView ], [[NAME, CHROME+' WebView'], VERSION], [ @@ -1232,10 +1237,16 @@ for (var i in brands) { var brandName = brands[i].brand || brands[i], brandVersion = brands[i].version; - if (this.itemType == UA_BROWSER && !/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && brandName != CHROMIUM))) { + if (this.itemType == UA_BROWSER && + !/not.a.brand/i.test(brandName) && + (!prevName || + (/Chrom/.test(prevName) && brandName != CHROMIUM) || + (prevName == EDGE && /WebView2/.test(brandName)) + )) { brandName = strMapper(brandName, { 'Chrome' : 'Google Chrome', 'Edge' : 'Microsoft Edge', + 'Edge WebView2' : 'Microsoft Edge WebView2', 'Chrome WebView' : 'Android WebView', 'Chrome Headless' : 'HeadlessChrome', 'Huawei Browser' : 'HuaweiBrowser', diff --git a/test/data/ua/browser/browser-all.json b/test/data/ua/browser/browser-all.json index 3fc0e5e57..9f39de901 100644 --- a/test/data/ua/browser/browser-all.json +++ b/test/data/ua/browser/browser-all.json @@ -2229,6 +2229,26 @@ "major" : "74" } }, + { + "desc" : "Microsoft Edge WebView", + "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", + "expect" : + { + "name" : "Edge WebView", + "version" : "18.17763", + "major" : "18" + } + }, + { + "desc" : "Microsoft Edge WebView2", + "ua" : "Mozilla/5.0 (Linux; Android 11; SM-G991B Build/RP1A.200720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.120 Mobile Safari/537.36 EdgW/1.0", + "expect" : + { + "name" : "Edge WebView2", + "version" : "91.0.4472.120", + "major" : "91" + } + }, { "desc" : "Iridium", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Iridium/43.8 Safari/537.36 Chrome/43.0.2357.132", diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 18dd09a4e..7d12d5023 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -313,6 +313,19 @@ describe('UA-CH Headers tests', () => { } } }, + { + headers : { + 'sec-ch-ua': '" Not;A Brand";v="99", "Microsoft Edge";v="103", "Chromium";v="103", "Microsoft Edge WebView2";v="104"' + }, + expect: { + browser : { + name : 'Edge WebView2', + version : '104', + major : '104', + type : undefined + } + } + }, { headers : { 'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "HuaweiBrowser";v="114"' From a3549efc223e5639af47142125c53224890b8b75 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 30 May 2025 23:16:02 +0700 Subject: [PATCH 364/388] [extensions] Add new bots: Daumoa, iAskBot, Iframely, Qwantbot --- src/extensions/ua-parser-extensions.js | 12 ++++- test/data/ua/extension/crawler.json | 70 ++++++++++++++++++++++++++ test/data/ua/extension/fetcher.json | 10 ++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 8317f4b21..a91bdd99c 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -48,13 +48,14 @@ const Crawlers = Object.freeze({ // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot + // iAskBot - https://iask.ai // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -68,6 +69,9 @@ const Crawlers = Object.freeze({ // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, + // Daum + /(daum(?:oa)?(?:-image)?)[ \/]([\w\.]+)/i, + // Facebook / Meta // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i, @@ -78,6 +82,9 @@ const Crawlers = Object.freeze({ // Internet Archive (archive.org) /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + // Qwantbot - https://help.qwant.com/bot + /(qwantbot)[-\w]*\/?([\w\.]*)/i, + // SemrushBot - http://www.semrush.com/bot.html /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, @@ -222,10 +229,11 @@ const Fetchers = Object.freeze({ // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan + // Iframely - https://iframely.com/docs/about // Perplexity-User - https://docs.perplexity.ai/guides/bots // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|iframely|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 8ed2d0099..bf29428c6 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -299,6 +299,36 @@ "type" : "crawler" } }, + { + "desc" : "Daum", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant;) Daum 4.1", + "expect" : + { + "name" : "Daum", + "version" : "4.1", + "type" : "crawler" + } + }, + { + "desc" : "Daumoa", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant; not on Windows server;) Daumoa 4.0", + "expect" : + { + "name" : "Daumoa", + "version" : "4.0", + "type" : "crawler" + } + }, + { + "desc" : "Daumoa-image", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant; not on Windows server;) Daumoa-image/1.0", + "expect" : + { + "name" : "Daumoa-image", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Diffbot", "ua" : "Diffbot/0.1", @@ -489,6 +519,16 @@ "type" : "crawler" } }, + { + "desc" : "iAskBot", + "ua" : "Mozilla/5.0 AppleWebKit/605.1.15 (KHTML, like Gecko; compatible; iAskBot/1.0; +https://iask.ai/) Chrome/120.0.6099.119 Safari/605.1.15", + "expect" : + { + "name" : "iAskBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "ImagesiftBot", "ua" : "Mozilla/5.0 (compatible; ImagesiftBot; +imagesift.com)", @@ -620,6 +660,36 @@ "type" : "crawler" } }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot/1.0_12345; +https://help.qwant.com/bot/)", + "expect" : + { + "name" : "Qwantbot", + "version" : "1.0_12345", + "type" : "crawler" + } + }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot-prod51071/1.0; +Qwantbot@qwant.com)", + "expect" : + { + "name" : "Qwantbot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot-news/2.0; +https://help.qwant.com/bot/)", + "expect" : + { + "name" : "Qwantbot", + "version" : "2.0", + "type" : "crawler" + } + }, { "desc" : "SemrushBot", "ua" : "Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 62154a995..4a053939a 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -129,6 +129,16 @@ "type" : "fetcher" } }, + { + "desc" : "Iframely", + "ua" : "Iframely/1.3.1 (+https://iframely.com/docs/about)", + "expect" : + { + "name" : "Iframely", + "version" : "1.3.1", + "type" : "fetcher" + } + }, { "desc" : "Meta-ExternalFetcher", "ua" : "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", From 72d0c2acb32917a9287b8cb838be015d8e5dcac9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 3 Jun 2025 11:03:45 +0700 Subject: [PATCH 365/388] [extensions] Add new crawler bots: ChatGLM, Onespot, Startpage --- src/extensions/ua-parser-extensions.js | 7 +++--- src/helpers/ua-parser-helpers.js | 3 +++ test/data/ua/extension/crawler.json | 30 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index a91bdd99c..b2e31fb5b 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -52,10 +52,11 @@ const Crawlers = Object.freeze({ // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html + // Onespot - https://www.onespot.com/identifying-traffic.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -100,8 +101,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |line|yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 64d7e90db..d14667b31 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -110,6 +110,9 @@ const isAIBot = (resultOrUA) => [ // You.com 'youbot', + // Zhipu AI + 'chatglm-spider', + // Zyte 'scrapy' diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index bf29428c6..918ee9862 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -259,6 +259,16 @@ "type" : "crawler" } }, + { + "desc" : "ChatGLM-Spider", + "ua" : "Mozilla/5.0 (compatible; ChatGLM-Spider/1.0; +https://chatglm.cn/)", + "expect" : + { + "name" : "ChatGLM-Spider", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Coc Coc Bot (web)", "ua" : "Mozilla/5.0 (compatible; coccocbot-web/1.0; +http://help.coccoc.com/searchengine)", @@ -620,6 +630,16 @@ "type" : "crawler" } }, + { + "desc" : "Onespot", + "ua" : "Mozilla/5.0 (compatible; Onespot-ScraperBot/1.0; +https://www.onespot.com/identifying-traffic.html)", + "expect" : + { + "name" : "Onespot-ScraperBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "OpenAI Search", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot", @@ -750,6 +770,16 @@ "type" : "crawler" } }, + { + "desc" : "Startpage", + "ua" : "StartpagePrivateImageProxy/3.0 (https://www.startpage.com/; support@startpage.com) aiohttp.client/3.11.11", + "expect" : + { + "name" : "StartpagePrivateImageProxy", + "version" : "3.0", + "type" : "crawler" + } + }, { "desc" : "Teoma", "ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)", From 5f1e498bec8751a2017c76e8efc25419a42ce3b7 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 6 Jun 2025 22:01:57 +0700 Subject: [PATCH 366/388] Move browser hints map --- src/main/ua-parser.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 1e6210c4a..b355b8083 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -313,6 +313,18 @@ 'xr' : ['VR', 'XR'], '?' : ['Desktop', 'Unknown'], '*' : undefined + }, + + browserHintsMap = { + 'Chrome' : 'Google Chrome', + 'Edge' : 'Microsoft Edge', + 'Edge WebView2' : 'Microsoft Edge WebView2', + 'Chrome WebView': 'Android WebView', + 'Chrome Headless':'HeadlessChrome', + 'Huawei Browser': 'HuaweiBrowser', + 'MIUI Browser' : 'Miui Browser', + 'Opera Mobi' : 'OperaMobile', + 'Yandex' : 'YaBrowser' }; ////////////// @@ -1243,21 +1255,14 @@ (/Chrom/.test(prevName) && brandName != CHROMIUM) || (prevName == EDGE && /WebView2/.test(brandName)) )) { - brandName = strMapper(brandName, { - 'Chrome' : 'Google Chrome', - 'Edge' : 'Microsoft Edge', - 'Edge WebView2' : 'Microsoft Edge WebView2', - 'Chrome WebView' : 'Android WebView', - 'Chrome Headless' : 'HeadlessChrome', - 'Huawei Browser' : 'HuaweiBrowser', - 'MIUI Browser' : 'Miui Browser', - 'Opera Mobi' : 'OperaMobile', - 'Yandex' : 'YaBrowser' - }); - this.set(NAME, brandName) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - prevName = brandName; + brandName = strMapper(brandName, browserHintsMap); + prevName = this.get(NAME); + if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) { + this.set(NAME, brandName) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + prevName = brandName; } if (this.itemType == UA_ENGINE && brandName == CHROMIUM) { this.set(VERSION, brandVersion); From 5e7d0b25a32b7b3303a46919c95c442643a4c05e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 7 Jun 2025 00:05:28 +0700 Subject: [PATCH 367/388] Improve device&OS detection: LG webOS TV --- src/main/ua-parser.js | 49 ++++++++++++++--------- test/data/ua/device/_others.json | 27 +++++++++++++ test/data/ua/device/lg.json | 18 +++++++++ test/data/ua/device/samsung.json | 9 +++++ test/data/ua/os/webos.json | 68 ++++++++++++++++++++++++++++---- 5 files changed, 145 insertions(+), 26 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b355b8083..19177da14 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -247,17 +247,25 @@ // assign given value, ignore regex match this[q[0]] = q[1]; } - } else if (q.length === 3) { - // check whether function or regex + } else if (q.length >= 3) { + // Check whether q[1] FUNCTION or REGEX if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { - // call function (usually string mapper) - this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; + if (q.length > 3) { + this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined; + } else { + // call function (usually string mapper) + this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; + } } else { - // sanitize match using given regex - this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; + if (q.length == 3) { + // sanitize match using given regex + this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; + } else if (q.length == 4) { + this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; + } else if (q.length > 4) { + this[q[0]] = match ? q[3].apply(this, [match.replace(q[1], q[2])].concat(q.slice(4))) : undefined; + } } - } else if (q.length === 4) { - this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; } } else { this[q] = match ? match : undefined; @@ -432,7 +440,6 @@ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [ - /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /(daum)apps[\/ ]([\w\.]+)/i, // Daum App @@ -440,7 +447,7 @@ /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter - /(instagram|snapchat)[\/ ]([-\w\.]+)/i // Instagram/Snapchat + /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna ], [NAME, VERSION, [TYPE, INAPP]], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [ @@ -550,7 +557,7 @@ /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [ /\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, - /samsung[- ]((?!sm-[lr])[-\w]+)/i, + /samsung[- ]((?!sm-[lr]|browser)[-\w]+)/i, /sec-(sgh\w+)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ @@ -607,9 +614,10 @@ /\b(opd2(\d{3}a?))(?: bui|\))/i ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [ - // BLU Vivo Series - /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i - ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ + // BLU + /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i // Vivo series + ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ + // Vivo /; vivo (\w+)(?: bui|\))/i, /\b(v[12]\d{3}\w?[at])(?: bui|;)/i @@ -638,7 +646,7 @@ /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, - /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch))(\w+)/i, + /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i, /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ @@ -823,7 +831,7 @@ ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices - ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ + ], [[VENDOR, /.+\/(\w+)/, '$1', strMapper, {'LG':'lge'}], [MODEL, trim], [TYPE, SMARTTV]], [ // SmartTV from Unidentified Vendors /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i ], [MODEL, [TYPE, SMARTTV]], [ @@ -972,7 +980,7 @@ ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen - /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen)\w*[-\/\.; ]?([\d\.]*)/i ], [NAME, VERSION], [ /\(bb(10);/i // BlackBerry 10 ], [VERSION, [NAME, BLACKBERRY]], [ @@ -980,9 +988,12 @@ ], [VERSION, [NAME, 'Symbian']], [ /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS ], [VERSION, [NAME, FIREFOX+' OS']], [ - /web0s;.+rt(tv)/i, - /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS + /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS + /webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i ], [VERSION, [NAME, 'webOS']], [ + /web0s;.+?(?:chr[o0]me|safari)\/(\d+)/i + // https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine + ], [[VERSION, strMapper, {'25':'120','24':'108','23':'94','22':'87','6':'79','5':'68','4':'53','3':'38','2':'538','1':'537','*':'TV'}], [NAME, 'webOS']], [ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS ], [VERSION, [NAME, 'watchOS']], [ diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index c58a01e95..a270c00d9 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -290,6 +290,33 @@ "type": "smarttv" } }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 ( DRM; LGE/ATMACA/GRAETZ; GR32S1470; WEBOS22 04.41.53; W22_K8AP; DTV_C22L;) LaTivu_1.0.1_2022", + "expect": { + "vendor": "GRAETZ", + "model": "GR32S1470", + "type": "smarttv" + } + }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 HbbTV/1.5.1 (+DRM; LGE/DUALSHINE/SKYTECH; ST-5090; WEBOS5.0 04.50.63; W50_K6LP; DTV_C20P;)", + "expect": { + "vendor": "SKYTECH", + "model": "ST-5090", + "type": "smarttv" + } + }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 (+DRM; LGE/SILICONPLAYER/Hyundai; 50HYN3205; WEBOS22 04.42.26; W22_K8LP; DTV_C22P;)", + "expect": { + "vendor": "Hyundai", + "model": "50HYN3205", + "type": "smarttv" + } + }, { "desc": "PDA with Windows CE", "ua": "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", diff --git a/test/data/ua/device/lg.json b/test/data/ua/device/lg.json index 9d63ac146..9f125d72b 100644 --- a/test/data/ua/device/lg.json +++ b/test/data/ua/device/lg.json @@ -143,6 +143,24 @@ "type": "smarttv" } }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/53.0.2785.34 Safari/537.36 LG Browser/8.00.00(LGE; 32LM627BPSB; 05.40.45; 1; DTV_W19R); webOS.TV-2019; LG NetCast.TV-2013 Compatible (LGE, 32LM627BPSB, wireless)", + "expect": { + "vendor": "LG", + "model": "32LM627BPSB", + "type": "smarttv" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 HbbTV/1.3.1 ( DRM; LGE; OLED55B7V_Z; WEBOS3.5 06.10.60; W3_M16P; ) FVC/2.0 (LGE; WEBOS3.5 ;)", + "expect": { + "vendor": "LG", + "model": "OLED55B7V_Z", + "type": "smarttv" + } + }, { "desc": "LG Android TV", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; LG Android TV Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", diff --git a/test/data/ua/device/samsung.json b/test/data/ua/device/samsung.json index 56e7b73d2..36b68db0c 100644 --- a/test/data/ua/device/samsung.json +++ b/test/data/ua/device/samsung.json @@ -296,6 +296,15 @@ "type": "smarttv" } }, + { + "desc": "Samsung SmartTV", + "ua": "Mozilla/5.0 (SMART-TV; Linux; Tizen 8.0) AppleWebKit/537.36 (KHTML, like Gecko) Samsung Browser/7.0 Chrome/108.0.5359.1 TV Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "undefined", + "type": "smarttv" + } + }, { "desc": "Samsung SmartTV HBBTV", "ua": "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", diff --git a/test/data/ua/os/webos.json b/test/data/ua/os/webos.json index c31cd3f65..7f3d04ad4 100644 --- a/test/data/ua/os/webos.json +++ b/test/data/ua/os/webos.json @@ -1,6 +1,6 @@ [ { - "desc" : "WebOS", + "desc" : "HP WebOS", "ua" : "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.5; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.83 Safari/534.6 TouchPad/1.0", "expect" : { @@ -9,7 +9,7 @@ } }, { - "desc" : "WebOS", + "desc" : "Palm WebOS", "ua" : "Mozilla/5.0 (webOS/1.4.5; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.0", "expect" : { @@ -17,13 +17,58 @@ "version" : "1.4.5" } }, + { + "desc" : "WebOS TV 22", + "ua" : " Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/87.0.4280.88 Safari/537.36 LG Browser/8.00.00(LGE; ST50K-LG2200WEB; 04.42.26; 0x00000001; DTV_C22P); webOS.TV-2022; LG NetCast.TV-2013 Compatible (LGE, ST50K-LG2200WEB, wireless)", + "expect" : + { + "name" : "webOS", + "version" : "22" + } + }, + { + "desc" : "WebOS TV 22", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 (+DRM; LGE/SILICONPLAYER/Hyundai; 50HYN3205; WEBOS22 04.42.26; W22_K8LP; DTV_C22P;)", + "expect" : + { + "name" : "webOS", + "version" : "22" + } + }, + { + "desc" : "WebOS TV 5.0", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/68.0.3440.106 Safari/537.36 LG Browser/8.00.00(LGE; SWU-6522; 04.50.63; 0x00000001; DTV_C20P); webOS.TV-2020; LG NetCast.TV-2013 Compatible (LGE, SWU-6522, wireless)", + "expect" : + { + "name" : "webOS", + "version" : "5" + } + }, + { + "desc" : "WebOS TV 5.0", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 HbbTV/1.5.1 (+DRM; LGE/WALTON/Hyundai; 50HYN3205; WEBOS5.0 04.50.63; W50_K6LP; DTV_C20P;)", + "expect" : + { + "name" : "webOS", + "version" : "5.0" + } + }, { "desc" : "WebOS TV 5.x", "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 WebAppManager", "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "5" + } + }, + { + "desc" : "WebOS TV 5.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/68.0.3440.106 Safari/537.36 LG Browser/8.00.00(LGE; E65A71B-S; 04.50.63; 0x00000001; DTV_C20P); webOS.TV-2020; LG NetCast.TV-2013 Compatible (LGE, E65A71B-S, wired)", + "expect" : + { + "name" : "webOS", + "version" : "5" } }, { @@ -32,7 +77,16 @@ "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "4" + } + }, + { + "desc" : "WebOS TV 3.5", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 HbbTV/1.3.1 ( DRM; LGE; OLED55B7V-T; WEBOS3.5 06.10.60; W3_M16P; ) FVC/2.0 (LGE; WEBOS3.5 ;)", + "expect" : + { + "name" : "webOS", + "version" : "3.5" } }, { @@ -41,7 +95,7 @@ "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "3" } }, { @@ -50,7 +104,7 @@ "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "2" } }, { @@ -59,7 +113,7 @@ "expect" : { "name" : "webOS", - "version" : "TV" + "version" : "1" } } ] \ No newline at end of file From e31229828318b813e99b3598928d6aa69b8d14ca Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 10 Jun 2025 23:03:16 +0700 Subject: [PATCH 368/388] Fix #794: Detect Windows 7 --- src/main/ua-parser.js | 2 +- test/data/ua/os/windows.json | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 19177da14..43f6a1ae8 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -943,7 +943,7 @@ os : [[ // Windows - /microsoft (windows) (vista|xp)/i // Windows (iTunes) + /microsoft (windows) (7|vista|xp)/i // Windows ], [NAME, VERSION], [ /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json index 39e954155..1173baf57 100644 --- a/test/data/ua/os/windows.json +++ b/test/data/ua/os/windows.json @@ -53,6 +53,15 @@ "version" : "Vista" } }, + { + "desc" : "Windows 7", + "ua" : "Microsoft Windows 7", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, { "desc" : "Windows 7", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", From 1282175bac96e6be108be067ce451f59d37c0d85 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 10 Jun 2025 23:05:03 +0700 Subject: [PATCH 369/388] [extensions] Add new libraries: AdobeAIR, aiohttp, nutch, httpx, urllib3 --- src/extensions/ua-parser-extensions.js | 5 +-- test/data/ua/extension/library.json | 50 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index b2e31fb5b..7fff4c088 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -339,8 +339,9 @@ const Libraries = Object.freeze({ browser : [ // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent [ - /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:urllib|requests)|scrapy)\/([\w\.]+)/i, - /(jsdom)\/([\w\.]+)/i, + /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i, + /(adobeair|aiohttp|jsdom)\/([\w\.]+)/i, + /(nutch)-([\w\.-]+)(\(|$)/i, /\((java)\/([\w\.]+)/i ], [NAME, VERSION, [TYPE, LIBRARY]] ] diff --git a/test/data/ua/extension/library.json b/test/data/ua/extension/library.json index f6911055d..17435a28f 100644 --- a/test/data/ua/extension/library.json +++ b/test/data/ua/extension/library.json @@ -1,4 +1,24 @@ [ + { + "desc" : "AdobeAIR", + "ua" : "Mozilla/5.0 (Windows; U; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) AdobeAIR/3.1", + "expect" : + { + "name" : "AdobeAIR", + "version" : "3.1", + "type" : "library" + } + }, + { + "desc" : "aiohttp", + "ua" : "Python/3.9 aiohttp/3.8.1", + "expect" : + { + "name" : "aiohttp", + "version" : "3.8.1", + "type" : "library" + } + }, { "desc" : "Apache-HttpClient", "ua" : "Apache-HttpClient/4.5.14 (Java/17.0.12)", @@ -109,6 +129,16 @@ "type" : "library" } }, + { + "desc" : "Nutch", + "ua" : "AliyunSecBot/Nutch-1.21-SNAPSHOT", + "expect" : + { + "name" : "Nutch", + "version" : "1.21-SNAPSHOT", + "type" : "library" + } + }, { "desc" : "OkHttp", "ua" : "okhttp/3.2.0", @@ -149,6 +179,16 @@ "type" : "library" } }, + { + "desc" : "Python httpx", + "ua" : "python-httpx/0.27.2", + "expect" : + { + "name" : "python-httpx", + "version" : "0.27.2", + "type" : "library" + } + }, { "desc" : "Python urllib", "ua" : "Python-urllib/2.6", @@ -159,6 +199,16 @@ "type" : "library" } }, + { + "desc" : "Python urllib3", + "ua" : "python-urllib3/1.26.18", + "expect" : + { + "name" : "python-urllib3", + "version" : "1.26.18", + "type" : "library" + } + }, { "desc" : "Python requests", "ua" : "python-requests/2.32", From d28d74fea7864888747b4a7cce73e8e84c50467d Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 17 Jun 2025 01:47:24 +0700 Subject: [PATCH 370/388] [extensions] Add new apps: Discord, Evernote, Figma, Flipboard, Mattermost, Notion, Postman, Rambox, Rocket.Chat, Teams, TikTok Lite, VS Code --- src/extensions/ua-parser-extensions.js | 26 +++- test/data/ua/extension/inapp.json | 170 +++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 4 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 7fff4c088..345c88240 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -268,13 +268,31 @@ const Fetchers = Object.freeze({ /////////////////// const InApps = Object.freeze({ - browser : [ + browser : [[ + // Discord/Figma/Flipboard/Mattermost/Notion/Postman/Rambox/Rocket.Chat/Slack/Teams + /\b(discord|figma|mattermost|notion|postman|rambox|rocket.chat|slack|teams)\/([\w\.]+).+(electron\/|; ios)/i, + /(flipboard)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, INAPP]], [ + + // Evernote/Teams on mobile + /(evernote) win/i, + /(teams)mobile-(ios|and)/i + ], [NAME, [TYPE, INAPP]], [ + // Slack - [/(?:slack(?=.+electron|.+ios)|chatlyio)\/([\d\.]+)/i], - [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], + /chatlyio\/([\d\.]+)/i], + [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], [ + + // TikTok Lite + /ultralite app_version\/([\w\.]+)/i], + [VERSION, [NAME, 'TikTok Lite'], [TYPE, INAPP]], [ + + // VS Code + /\) code\/([\d\.]+).+electron\//i], + [VERSION, [NAME, 'VS Code'], [TYPE, INAPP]], [ // Yahoo! Japan - [/jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], + /jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], [VERSION, [NAME, 'Yahoo! Japan'], [TYPE, INAPP]] ] }); diff --git a/test/data/ua/extension/inapp.json b/test/data/ua/extension/inapp.json index 55124651a..f1861c401 100644 --- a/test/data/ua/extension/inapp.json +++ b/test/data/ua/extension/inapp.json @@ -1,4 +1,154 @@ [ + { + "desc" : "Discord on Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) discord/0.0.26 Chrome/108.0.5359.215 Electron/22.3.2 Safari/537.36", + "expect" : + { + "name" : "discord", + "version" : "0.0.26", + "type" : "inapp" + } + }, + { + "desc" : "Discord on iPad", + "ua" : "Discord/52.0 (iPad; iOS 14.4; Scale/2.00)", + "expect" : + { + "name" : "Discord", + "version" : "52.0", + "type" : "inapp" + } + }, + { + "desc" : "Evernote on Windows", + "ua" : "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Evernote Windows/306387 (pt-PT, DDL); Windows/6.1.0 (Win32); Safari/537.36", + "expect" : + { + "name" : "Evernote", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Figma on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) Figma/99.0.0 Chrome/89.0.4389.128 Electron/12.0.9 Safari/537.36", + "expect" : + { + "name" : "Figma", + "version" : "99.0.0", + "type" : "inapp" + } + }, + { + "desc" : "Flipboard on Android", + "ua" : "Mozilla/5.0 (Linux; Android 8.0.0; SM-A720F Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36 Flipboard/4.1.13/4342,4.1.13.4342", + "expect" : + { + "name" : "Flipboard", + "version" : "4.1.13", + "type" : "inapp" + } + }, + { + "desc" : "Mattermost on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Mattermost/4.4.0 Chrome/76.0.3809.146 Electron/6.1.7 Safari/537.36", + "expect" : + { + "name" : "Mattermost", + "version" : "4.4.0", + "type" : "inapp" + } + }, + { + "desc" : "Mattermost on iPad", + "ua" : "Mattermost/1.49.1 (iPad; iOS 15.3.1; Scale/2.00)", + "expect" : + { + "name" : "Mattermost", + "version" : "1.49.1", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Teams/1.8.00.3758 Chrome/126.0.6478.261 Electron/31.7.7 Safari/537.36", + "expect" : + { + "name" : "Teams", + "version" : "1.8.00.3758", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on iPad", + "ua" : "Mozilla/5.0 (iPad; CPU OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 TeamsMobile-iOS", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on iPhone", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 TeamsMobile-iOS", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on Android", + "ua" : "Mozilla/5.0 (Linux; Android 8.1.0; SM-G610F Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 TeamsMobile-Android", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Notion on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Notion/2.0.23 Chrome/91.0.4472.164 Electron/13.6.9 Safari/537.36", + "expect" : + { + "name" : "Notion", + "version" : "2.0.23", + "type" : "inapp" + } + }, + { + "desc" : "Postman on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Postman/9.29.0 Chrome/94.0.4606.81 Electron/15.5.7 Safari/537.36", + "expect" : + { + "name" : "Postman", + "version" : "9.29.0", + "type" : "inapp" + } + }, + { + "desc" : "Rambox on mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Rambox/0.7.7 Chrome/78.0.3904.130 Electron/7.2.4 Safari/537.36", + "expect" : + { + "name" : "Rambox", + "version" : "0.7.7", + "type" : "inapp" + } + }, + { + "desc" : "Rocket.Chat on mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Rocket.Chat/3.8.16 Chrome/106.0.5249.199 Electron/21.3.3 Safari/537.36", + "expect" : + { + "name" : "Rocket.Chat", + "version" : "3.8.16", + "type" : "inapp" + } + }, { "desc" : "Slack on mac", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Slack/4.39.90 Chrome/127.0.6533.72 Electron/13.1.9 Safari/537.36", @@ -9,6 +159,26 @@ "type" : "inapp" } }, + { + "desc" : "TikTok Lite", + "ua" : "Mozilla/5.0 (Linux; Android 8.0.0; SM-J400F Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.91 Mobile Safari/537.36 Channel/release AppName/ultralite app_version/27.2.3 Region/ID ByteLocale/id-ID ByteFullLocale/id-ID", + "expect" : + { + "name" : "TikTok Lite", + "version" : "27.2.3", + "type" : "inapp" + } + }, + { + "desc" : "VS Code on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.85.1 Chrome/114.0.5735.289 Electron/25.9.7 Safari/537.36", + "expect" : + { + "name" : "VS Code", + "version" : "1.85.1", + "type" : "inapp" + } + }, { "desc" : "Yahoo! Japan on Android", "ua" : "Mozilla/5.0 (Linux; Android 13; SH-M20 Build/TKQ1.220915.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36 YJApp-ANDROID jp.co.yahoo.android.yjtop/3.187.0", From ee88f16620cb04723fb721b7112f0a7f408822e0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 18 Jun 2025 19:01:12 +0700 Subject: [PATCH 371/388] [extensions] Add new email: Apple's Mail, DaumMail, Polymail, ProtonMail, SparkDesktop, Zimbra, ZohoMail-Desktop --- src/extensions/ua-parser-extensions.js | 15 ++++-- test/data/ua/extension/email.json | 70 ++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 345c88240..d89b7ec55 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -212,9 +212,18 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ [ - // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird - /(airmail|bluemail|emclient|evolution|foxmail|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|thunderbird|yahoo)(?:m.+ail; |[\/ ])([\w\.]+)/i - ], [NAME, VERSION, [TYPE, EMAIL]] + // Evolution / Kontact/KMail[2] / [Microsoft/Mac] Outlook / Thunderbird + // Airmail / BlueMail / DaumMail / eMClient / Foxmail / NaverMailApp / Polymail + // ProtonMail / SparkDesktop / Sparrow / Yahoo! Mail / Zimbra / ZohoMail-Desktop + /((?:air|blue|daum|fox|poly|proton)mail|emclient|evolution|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|sparkdesktop|thunderbird|yahoo|zohomail-desktop)(?:m.+ail; |[\/ ])([\w\.]+)/i, + + // Apple's Mail + /(mail)\/([\w\.]+) cf/i + ], [NAME, VERSION, [TYPE, EMAIL]], [ + + // Zimbra + /zdesktop\/([\w\.]+)/i + ], [VERSION, [NAME, 'Zimbra'], [TYPE, EMAIL]] ] }); diff --git a/test/data/ua/extension/email.json b/test/data/ua/extension/email.json index 27fbce58f..8acd5d4e7 100644 --- a/test/data/ua/extension/email.json +++ b/test/data/ua/extension/email.json @@ -9,6 +9,16 @@ "type" : "email" } }, + { + "desc" : "Apple Mail", + "ua" : "Mail/3826.500.181.1.5 CFNetwork/3826.500.111.1.1 Darwin/24.4.0", + "expect" : + { + "name" : "Mail", + "version" : "3826.500.181.1.5", + "type" : "email" + } + }, { "desc" : "BlueMail", "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) BlueMail/0.10.31 Chrome/61.0.3163.100 Electron/2.0.18 Safari/537.36", @@ -29,6 +39,16 @@ "type" : "email" } }, + { + "desc" : "DaumMail", + "ua" : "DaumMobileApp (LG-KU5400; U; Android 2.3.7|10; ko-kr) DaumMail/1.0.8 ", + "expect" : + { + "name" : "DaumMail", + "version" : "1.0.8", + "type" : "email" + } + }, { "desc" : "Evolution", "ua" : "Evolution/3.52.3", @@ -119,6 +139,36 @@ "type" : "email" } }, + { + "desc" : "Polymail", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Polymail/2.3.12 Chrome/114.0.5735.134 Electron/25.2.0 Safari/537.36", + "expect" : + { + "name" : "Polymail", + "version" : "2.3.12", + "type" : "email" + } + }, + { + "desc" : "ProtonMail", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ProtonMail/1.4.0 Chrome/122.0.6261.156 Electron/29.4.6 Safari/537.36", + "expect" : + { + "name" : "ProtonMail", + "version" : "1.4.0", + "type" : "email" + } + }, + { + "desc" : "SparkDesktop", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) SparkDesktop/3.0.30 Chrome/102.0.5005.63 Electron/19.0.4 Safari/537.36", + "expect" : + { + "name" : "SparkDesktop", + "version" : "3.0.30", + "type" : "email" + } + }, { "desc" : "Sparrow", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Sparrow/1043.1", @@ -158,5 +208,25 @@ "version" : "1.3.10", "type" : "email" } + }, + { + "desc" : "Zimbra", + "ua" : "Mozilla/5.0 (Windows; U; Windows NT 6.2; it; rv:1.9.2.19pre) Gecko/20110902 Prism zdesktop/7.2.8", + "expect" : + { + "name" : "Zimbra", + "version" : "7.2.8", + "type" : "email" + } + }, + { + "desc" : "ZohoMail", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ZohoMail-Desktop/1.6.3 Chrome/124.0.6367.207 Electron/30.0.6 Safari/537.36", + "expect" : + { + "name" : "ZohoMail-Desktop", + "version" : "1.6.3", + "type" : "email" + } } ] From f300478bdc8ccdbabfbb2162d6f045525d94d281 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 19 Jun 2025 00:14:25 +0700 Subject: [PATCH 372/388] Identify device that uses Firefox Reality / Wolvic as an XR device --- src/main/ua-parser.js | 4 +++- test/data/ua/device/_others.json | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 43f6a1ae8..f34933ba5 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -886,6 +886,8 @@ ], [VENDOR, MODEL, [TYPE, XR]], [ /(quest( \d| pro)?s?).+vr/i // Meta Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ + /mobile vr; rv.+firefox/i // Unidentifiable VR device using Firefox Reality / Wolvic + ], [[TYPE, XR]], [ /////////////////// // EMBEDDED @@ -897,7 +899,7 @@ ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ /(homepod).+mac os/i // Apple HomePod ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [ - /windows iot/i + /windows iot/i // Unidentifiable embedded device using Windows IoT ], [[TYPE, EMBEDDED]], [ //////////////////// diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index a270c00d9..55329b3e3 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -290,6 +290,24 @@ "type": "smarttv" } }, + { + "desc": "Unknown VR Device using Firefox Reality", + "ua": "Mozilla/5.0 (Android 10; Mobile VR; rv:123.0) Gecko/123.0 Firefox/123.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "xr" + } + }, + { + "desc": "Unknown VR Device using Wolvic", + "ua": "Mozilla/5.0 (Android 14; Mobile VR; rv:128.0) Gecko/128.0 Firefox/128.0 Wolvic/1.8", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "xr" + } + }, { "desc": "Smart TV", "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 ( DRM; LGE/ATMACA/GRAETZ; GR32S1470; WEBOS22 04.41.53; W22_K8AP; DTV_C22L;) LaTivu_1.0.1_2022", From f9836f198199fc3d57542e1517a75dcc052331c9 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 19 Jun 2025 16:14:09 +0700 Subject: [PATCH 373/388] Add new vendor: Retroid --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 11 ++++++----- test/data/ua/device/retroid.json | 11 +++++++++++ 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 test/data/ua/device/retroid.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 44fefbf96..2b2b46669 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -244,6 +244,7 @@ export const Vendor: Readonly<{ PICO: "Pico"; POLYTRON: "Polytron"; REALME: "Realme"; + RETROID: "Retroid"; RIM: "RIM"; ROKU: "Roku"; SAMSUNG: "Samsung"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 3420acba4..c4d869c3c 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -255,6 +255,7 @@ const Vendor = Object.freeze({ PICO: 'Pico', POLYTRON: 'Polytron', REALME: 'Realme', + RETROID: 'Retroid', RIM: 'RIM', ROKU: 'Roku', SAMSUNG: 'Samsung', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index f34933ba5..69c44854e 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -842,15 +842,16 @@ // CONSOLES /////////////////// - /(ouya)/i, // Ouya - /(nintendo) (\w+)/i // Nintendo - ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ - /droid.+; (shield)( bui|\))/i // Nvidia Portable - ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ /(playstation \w+)/i // Playstation ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [ /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [ + /(ouya)/i, // Ouya + /(nintendo) (\w+)/i, // Nintendo + /(retroid) (pocket ([^\)]+))/i // Retroid Pocket + ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ + /droid.+; (shield)( bui|\))/i // Nvidia Portable + ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ /////////////////// // WEARABLES diff --git a/test/data/ua/device/retroid.json b/test/data/ua/device/retroid.json new file mode 100644 index 000000000..fc7a83daf --- /dev/null +++ b/test/data/ua/device/retroid.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Retroid Pocket 4 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 13; Retroid Pocket 4 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.96 YaBrowser/24.4.3.96.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Retroid", + "model": "Pocket 4 Pro", + "type": "console" + } + } +] \ No newline at end of file From a9378427eddb7a49395be6556c448b95814352fc Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 21 Jun 2025 13:31:52 +0700 Subject: [PATCH 374/388] Improve Windows detection & recognize Windows CE & RT as separate OS variants --- src/enums/ua-parser-enums.d.ts | 2 + src/enums/ua-parser-enums.js | 2 + src/main/ua-parser.js | 39 +++-- test/data/ua/os/windows-ce.json | 20 +++ test/data/ua/os/windows-mobile.json | 18 ++ test/data/ua/os/windows-phone.json | 31 +++- test/data/ua/os/windows-rt.json | 20 +++ test/data/ua/os/windows.json | 246 ++++++++++++++++++++++++++-- 8 files changed, 343 insertions(+), 35 deletions(-) create mode 100644 test/data/ua/os/windows-ce.json create mode 100644 test/data/ua/os/windows-rt.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 2b2b46669..97c760aa1 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -373,9 +373,11 @@ export const OS: Readonly<{ WATCHOS: "watchOS"; WEBOS: "WebOS"; WINDOWS: "Windows"; + WINDOWS_CE: "Windows CE"; WINDOWS_IOT: "Windows IoT"; WINDOWS_MOBILE: "Windows Mobile"; WINDOWS_PHONE: "Windows Phone"; + WINDOWS_RT: "Windows RT"; XBOX: "Xbox"; XUBUNTU: "Xubuntu"; ZENWALK: "Zenwalk"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index c4d869c3c..803970c09 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -388,9 +388,11 @@ const OS = Object.freeze({ WATCHOS: 'watchOS', WEBOS: 'WebOS', WINDOWS: 'Windows', + WINDOWS_CE: 'Windows CE', WINDOWS_IOT: 'Windows IoT', WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', + WINDOWS_RT: 'Windows RT', XBOX: 'Xbox', XUBUNTU: 'Xubuntu', ZENWALK: 'Zenwalk' diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 69c44854e..e23ebb5c5 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -299,17 +299,17 @@ ////////////// var windowsVersionMap = { - 'ME' : '4.90', - 'NT 3.11' : 'NT3.51', - 'NT 4.0' : 'NT4.0', - '2000' : 'NT 5.0', - 'XP' : ['NT 5.1', 'NT 5.2'], - 'Vista' : 'NT 6.0', - '7' : 'NT 6.1', - '8' : 'NT 6.2', - '8.1' : 'NT 6.3', - '10' : ['NT 6.4', 'NT 10.0'], - 'RT' : 'ARM' + 'ME' : '4.90', + 'NT 3.51': '3.51', + 'NT 4.0': '4.0', + '2000' : ['5.0', '5.01'], + 'XP' : ['5.1', '5.2'], + 'Vista' : '6.0', + '7' : '6.1', + '8' : '6.2', + '8.1' : '6.3', + '10' : ['6.4', '10.0'], + 'NT' : '' }, formFactorsMap = { @@ -946,14 +946,17 @@ os : [[ // Windows - /microsoft (windows) (7|vista|xp)/i // Windows + /(windows nt) (6\.[23]); arm/i // Windows RT + ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [ + /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone + // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11 + /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|vista|xp)/i + ], [NAME, VERSION], [ + /windows nt ?([\d\.\)]*)(?!.+xbox)/i, + /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i + ], [[VERSION, /(;|\))/g, '', strMapper, windowsVersionMap], [NAME, WINDOWS]], [ + /(windows ce)\/?([\d\.]*)/i // Windows CE ], [NAME, VERSION], [ - /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone - ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ - /windows nt 6\.2; (arm)/i, // Windows RT - /windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i, - /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i - ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ // iOS/macOS /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS diff --git a/test/data/ua/os/windows-ce.json b/test/data/ua/os/windows-ce.json new file mode 100644 index 000000000..a28eb17fd --- /dev/null +++ b/test/data/ua/os/windows-ce.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Windows CE", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)", + "expect" : + { + "name" : "Windows CE", + "version" : "undefined" + } + }, + { + "desc" : "Windows CE", + "ua" : "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", + "expect" : + { + "name" : "Windows CE", + "version" : "1.0.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows-mobile.json b/test/data/ua/os/windows-mobile.json index 5baf78c67..78df88b1a 100644 --- a/test/data/ua/os/windows-mobile.json +++ b/test/data/ua/os/windows-mobile.json @@ -16,5 +16,23 @@ "name" : "Windows Mobile", "version" : "undefined" } + }, + { + "desc" : "Windows Mobile", + "ua" : "Opera/9.7 (Windows Mobile; PPC; Opera Mobi/35166; U; en) Presto/2.2.1", + "expect" : + { + "name" : "Windows Mobile", + "version" : "undefined" + } + }, + { + "desc" : "Windows Mobile 6.1", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11) 320x240; VZW; Motorola-Q9c; Windows Mobile 6.1 Standard", + "expect" : + { + "name" : "Windows Mobile", + "version" : "6.1" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/windows-phone.json b/test/data/ua/os/windows-phone.json index a6a8b8212..bf4bd0a26 100644 --- a/test/data/ua/os/windows-phone.json +++ b/test/data/ua/os/windows-phone.json @@ -9,14 +9,32 @@ } }, { - "desc" : "Windows Phone OS", + "desc" : "Windows Phone 6.5", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; HTC_Touch2_T3333; Windows Phone 6.5)", + "expect" : + { + "name" : "Windows Phone", + "version" : "6.5" + } + }, + { + "desc" : "Windows Phone 7.0", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; DELL; Venue Pro)", "expect" : { - "name" : "Windows Phone OS", + "name" : "Windows Phone", "version" : "7.0" } }, + { + "desc" : "Windows Phone 7.5", + "ua" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; NOKIA; Lumia 800)", + "expect" : + { + "name" : "Windows Phone", + "version" : "7.5" + } + }, { "desc" : "Windows Phone 8", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; HTC; Windows Phone 8X by HTC)", @@ -25,5 +43,14 @@ "name" : "Windows Phone", "version" : "8.0" } + }, + { + "desc" : "Windows Phone 8.1", + "ua" : "Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537", + "expect" : + { + "name" : "Windows Phone", + "version" : "8.1" + } } ] \ No newline at end of file diff --git a/test/data/ua/os/windows-rt.json b/test/data/ua/os/windows-rt.json new file mode 100644 index 000000000..8bee15174 --- /dev/null +++ b/test/data/ua/os/windows-rt.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Windows RT 8", + "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", + "expect" : + { + "name" : "Windows RT", + "version" : "8" + } + }, + { + "desc" : "Windows RT 8.1", + "ua" : "Mozilla/5.0 (Windows NT 6.3; ARM; Trident/7.0; Touch; rv:11.0) like Gecko", + "expect" : + { + "name" : "Windows RT", + "version" : "8.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json index 1173baf57..f2f747def 100644 --- a/test/data/ua/os/windows.json +++ b/test/data/ua/os/windows.json @@ -1,4 +1,103 @@ [ + { + "desc" : "Windows 3.1", + "ua" : "NCSA_Mosaic/2.0 (Windows 3.1)", + "expect" : + { + "name" : "Windows", + "version" : "3.1" + } + }, + { + "desc" : "Windows 3.1", + "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 3.1)", + "expect" : + { + "name" : "Windows", + "version" : "3.1" + } + }, + { + "desc" : "Windows NT", + "ua" : "Mozilla/4.51 [de] (WinNT; I)", + "expect" : + { + "name" : "Windows", + "version" : "NT" + } + }, + { + "desc" : "Windows NT 3.51", + "ua" : "Mozilla/4.0 (compatible; MSIE 4.0; Windows NT)", + "expect" : + { + "name" : "Windows", + "version" : "NT" + } + }, + { + "desc" : "Windows NT 3.51", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.05; Windows NT 3.51)", + "expect" : + { + "name" : "Windows", + "version" : "NT 3.51" + } + }, + { + "desc" : "Windows NT 4.0", + "ua" : "Opera/8.41.(Windows NT 4.0; ts-ZA) Presto/2.9.178 Version/11.00", + "expect" : + { + "name" : "Windows", + "version" : "NT 4.0" + } + }, + { + "desc" : "Windows NT 4.0", + "ua" : "Mozilla/5.0 (Windows; U; WinNT4.0; de-DE; rv:1.7.5) Gecko/20041108 Firefox/52.7.4", + "expect" : + { + "name" : "Windows", + "version" : "NT 4.0" + } + }, + { + "desc" : "Netscape on Windows 95", + "ua" : "Mozilla/5.0 (Windows; U; Win95; de-DE; rv:0.9.2) Gecko/20010726 Netscape6/6.1", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/3.0 (Win95)", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/3.0 (compatible; Opera/3.0; Windows 95/NT4) 3.2", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.0; Windows 95) Opera 6.02 [en]", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, { "desc" : "Windows 95", "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)", @@ -17,6 +116,24 @@ "version" : "98" } }, + { + "desc" : "Firebird on Windows 98", + "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031007 Firebird/0.7", + "expect" : + { + "name" : "Windows", + "version" : "98" + } + }, + { + "desc" : "K-Meleon on Windows 98", + "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031016 K-Meleon/0.8.2", + "expect" : + { + "name" : "Windows", + "version" : "98" + } + }, { "desc" : "Windows ME", "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90) Gecko/20020502 CS 2000 7.0/7.0", @@ -26,6 +143,51 @@ "version" : "ME" } }, + { + "desc" : "Opera on Windows ME", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.0; Windows ME) Opera 5.12 [de]", + "expect" : + { + "name" : "Windows", + "version" : "ME" + } + }, + { + "desc" : "Netscape on Windows ME", + "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90; en-US; rv:1.8.1.8pre) Gecko/20071015 Firefox/2.0.0.7 Navigator/9.0", + "expect" : + { + "name" : "Windows", + "version" : "ME" + } + }, + { + "desc" : "Netscape on Windows 2000", + "ua" : "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.5) Gecko/20050519 Netscape/8.0.1", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Opera on Windows 2000", + "ua" : "Opera/6.05 (Windows 2000; U)", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Opera on Windows 2000", + "ua" : "Opera/9.69 (Windows NT 5.01; en-US) Presto/2.8.160 Version/10.00", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, { "desc" : "Windows 2000", "ua" : "Mozilla/3.0 (compatible; MSIE 3.0; Windows NT 5.0)", @@ -44,6 +206,15 @@ "version" : "XP" } }, + { + "desc" : "Windows XP", + "ua" : "Mozilla/5.0 (Windows XP; U) Opera 6.05 [de]", + "expect" : + { + "name" : "Windows", + "version" : "XP" + } + }, { "desc" : "Windows Vista", "ua" : "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; fr-FR)", @@ -62,6 +233,15 @@ "version" : "7" } }, + { + "desc" : "Windows 7", + "ua" : "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Evernote Windows/306387 (pt-PT, DDL); Windows/6.1.0 (Win32); Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, { "desc" : "Windows 7", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", @@ -80,6 +260,15 @@ "version" : "8" } }, + { + "desc" : "Windows 8.1", + "ua" : "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:66.0.5) Gecko/20100101 Firefox/66.0.5", + "expect" : + { + "name" : "Windows", + "version" : "8.1" + } + }, { "desc" : "Windows 10", "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", @@ -108,48 +297,75 @@ } }, { - "desc" : "Windows RT", - "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", + "desc" : "iTunes on Windows Vista", + "ua" : "iTunes/10.7 (Windows; Microsoft Windows Vista Home Premium Edition Service Pack 1 (Build 6001)) AppleWebKit/536.26.9", "expect" : { "name" : "Windows", - "version" : "RT" + "version" : "Vista" } }, { - "desc" : "Windows CE", - "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)", + "desc" : "iTunes on Windows 7", + "ua" : "iTunes/10.6.3 (Windows; Microsoft Windows 7 x64 Business Edition Service Pack 1 (Build 7601)) AppleWebKit/534.57.2", "expect" : { "name" : "Windows", - "version" : "CE" + "version" : "7" } }, { - "desc" : "Windows NT on x86 or aarch64 CPU using Firefox", - "ua" : "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0", + "desc" : "iTunes on Windows 8", + "ua" : "iTunes/12.1.1 (Windows; Microsoft Windows 8 x64 Business Edition (Build 9200)) AppleWebKit/7600.1017.9000.2", "expect" : { "name" : "Windows", - "version" : "NT x" + "version" : "8" } }, { - "desc" : "Windows NT on x64 CPU using Firefox", - "ua" : "Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0", + "desc" : "iTunes on Windows 8.1", + "ua" : "iTunes/12.4 (Windows; Microsoft Windows 8.1 x64 Business Edition (Build 9200); x64) AppleWebKit/7601.6016.1000.1", "expect" : { "name" : "Windows", - "version" : "NT x" + "version" : "8.1" } }, { - "desc" : "iTunes Windows Vista", - "ua" : "iTunes/10.7 (Windows; Microsoft Windows Vista Home Premium Edition Service Pack 1 (Build 6001)) AppleWebKit/536.26.9", + "desc" : "iTunes on Windows 10", + "ua" : "iTunes/12.9.1 (Windows; Microsoft Windows 10 x64 Professional Edition (Build 18362); x64) AppleWebKit/7606.2104.0.21", "expect" : { "name" : "Windows", - "version" : "Vista" + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 10", + "ua" : "iTunes/12.6.3 (Windows; Microsoft Windows 10.0 x64 (Build 17763); x64) AppleWebKit/7604.1038.1006.6", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 10 S", + "ua" : "iTunes/12.12 (Windows; Microsoft Windows 10 S x64; x64) AppleWebKit/7613.2007", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 11", + "ua" : "iTunes/12.13 (Windows; Microsoft Windows 11 x64; x64) AppleWebKit/7613.2007", + "expect" : + { + "name" : "Windows", + "version" : "11" } } ] \ No newline at end of file From 86b3cd37e25d1958f9a59054b6d3afb3abf8a2a5 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 21 Jun 2025 21:37:07 +0700 Subject: [PATCH 375/388] Improve OS detection: Windows Server series --- src/main/ua-parser.js | 2 +- test/data/ua/os/windows.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index e23ebb5c5..e3373d2ed 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -950,7 +950,7 @@ ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [ /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11 - /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|vista|xp)/i + /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|server 20\d\d( r2)?|vista|xp)/i ], [NAME, VERSION], [ /windows nt ?([\d\.\)]*)(?!.+xbox)/i, /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json index f2f747def..faa965289 100644 --- a/test/data/ua/os/windows.json +++ b/test/data/ua/os/windows.json @@ -242,6 +242,15 @@ "version" : "7" } }, + { + "desc" : "Windows 7", + "ua" : "Mozilla/5.0 (Windows 7 Enterprise; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6099.71 Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, { "desc" : "Windows 7", "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", @@ -278,6 +287,15 @@ "version" : "10" } }, + { + "desc" : "Windows Server 2012 R2", + "ua" : "Mozilla/5.0 (Windows Server 2012 R2 Standard; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5975.80 Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "Server 2012 R2" + } + }, { "desc" : "WeChat Desktop for Windows Built-in Browser", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", From c9d008e97af92aa860f3361964e1fb0e485fca34 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 21 Jun 2025 21:41:00 +0700 Subject: [PATCH 376/388] Bump version `2.0.4` --- CHANGELOG.md | 21 ++ dist/ua-parser.min.js | 4 +- dist/ua-parser.min.mjs | 4 +- dist/ua-parser.pack.js | 4 +- dist/ua-parser.pack.mjs | 4 +- package-lock.json | 4 +- package.json | 2 +- src/enums/ua-parser-enums.d.ts | 2 +- src/enums/ua-parser-enums.js | 2 +- src/enums/ua-parser-enums.mjs | 14 +- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 2 +- src/extensions/ua-parser-extensions.mjs | 68 +++++-- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 5 +- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 237 +++++++++++++---------- 19 files changed, 251 insertions(+), 134 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2fa9b76..d1f9ac63d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,27 @@ --- +## Version 2.0.4 + +- Add new browser: Edge WebView, Edge WebView2 +- Add new device vendor: Lava, Retroid, Vizio +- Add new OS: ArcaOS, Knoppix, Xubuntu, Windows CE, Windows RT +- Improve device detection: Google Pixel & Pixelbook Series, HMD, Infinix, LG WebOS TV, Motorola, Nothing, OnePlus, Sony, Tecno +- Improve OS detection: AIX, Arch, Fuchsia, Haiku, HarmonyOS, Mint, MorphOS, Solaris, Windows +- Improve `withClientHints()` browser naming adjustments: `Microsoft Edge WebView2` => `Edge WebView2` +- Identify device that uses Firefox Reality / Wolvic as `xr` +- Identify device with large screen as `smarttv` +- Identify Windows CE & Windows RT as distinct OS variants +- `extensions` submodule: + - Remove lookbehind assertion to ensure regex compatibility + - Add new crawlers: ChatGLM, Daum, iAskBot, Onespot, Qwantbot, Startpage + - Add new emails: Apple's Mail, DaumMail, Polymail, ProtonMail, SparkDesktop, Zimbra, ZohoMail-Desktop + - Add new fetchers: Iframely, MistralAI-User, Perplexity-User + - Add new inApps: Discord, Evernote, Figma, Flipboard, Mattermost, Notion, Postman, Rambox, Rocket.Chat, Microsoft Teams, TikTok Lite, VS Code + - Add new libraries: AdobeAIR, aiohttp, nutch, httpx, urllib3 +- `enum` submodule: + - Fix mistakenly placed `BLU` categorized as browser name instead of device vendor + ## Version 2.0.3 - Add new browser: Dooble, Ecosia, LG Browser, Otter, qutebrowser, Surf diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 0c7f4071e..7d65516a5 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.3",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome","Huawei Browser":"HuaweiBrowser","MIUI Browser":"Miui Browser","Opera Mobi":"OperaMobile",Yandex:"YaBrowser"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){this[q[0]]=match?match.replace(q[1],q[2]):undefined}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}else if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/Chrom/.test(prevName)&&brandName!=CHROMIUM||prevName==EDGE&&/WebView2/.test(brandName))){brandName=strMapper(brandName,browserHintsMap);prevName=this.get(NAME);if(!(prevName&&!/Chrom/.test(prevName)&&/Chrom/.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.min.mjs b/dist/ua-parser.min.mjs index b86273f46..3cb08310a 100644 --- a/dist/ua-parser.min.mjs +++ b/dist/ua-parser.min.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -var LIBVERSION="2.0.3",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str1[i])==lowerize(str2))return true}return false}return isString(str1)?lowerize(str2).indexOf(lowerize(str1))!==-1:false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/chrom/i.test(prevName)&&brandName!=CHROMIUM)){brandName=strMapper(brandName,{Chrome:"Google Chrome",Edge:"Microsoft Edge","Chrome WebView":"Android WebView","Chrome Headless":"HeadlessChrome","Huawei Browser":"HuaweiBrowser","MIUI Browser":"Miui Browser","Opera Mobi":"OperaMobile",Yandex:"YaBrowser"});this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion));prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file +var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){this[q[0]]=match?match.replace(q[1],q[2]):undefined}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}else if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/Chrom/.test(prevName)&&brandName!=CHROMIUM||prevName==EDGE&&/WebView2/.test(brandName))){brandName=strMapper(brandName,browserHintsMap);prevName=this.get(NAME);if(!(prevName&&!/Chrom/.test(prevName)&&/Chrom/.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index ab102f131..94f5cc53d 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -((i,l)=>{function U(i){for(var e={},t=0;t{var t,o={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ii,e):Ii,j.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Bi(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Bi(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>E?Hi(i,E):i),this}]]).setUA(r),this):new I(i,e,t).getResult()}I.VERSION="2.0.3",I.BROWSER=U([v,y,L,k]),I.CPU=U([C]),I.DEVICE=U([T,x,k,G,S,e,r,t,D]),I.ENGINE=I.OS=U([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=I:exports).UAParser=I:typeof define===R&&define.amd?define(function(){return I}):Ti&&(i.UAParser=I);var Ri,Vi=Ti&&(i.jQuery||i.Zepto);Vi&&!Vi.ua&&(Ri=new I,Vi.ua=Ri.getResult(),Vi.ua.get=function(){return Ri.getUA()},Vi.ua.set=function(i){Ri.setUA(i);var e,t=Ri.getResult();for(e in t)Vi.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file +((i,l)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!Ti(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Pi,e):Pi,M.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Gi(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Gi(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return O(i)&&(r=i.length>U?Mi(i,U):i),this}]]).setUA(r),this):new V(i,e,t).getResult()}V.VERSION="2.0.4",V.BROWSER=I([v,y,G,k]),V.CPU=I([C]),V.DEVICE=I([S,x,k,W,_,e,r,t,F]),V.ENGINE=V.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=V:exports).UAParser=V:typeof define===R&&define.amd?define(function(){return V}):qi&&(i.UAParser=V);var Wi,Ni=qi&&(i.jQuery||i.Zepto);Ni&&!Ni.ua&&(Wi=new V,Ni.ua=Wi.getResult(),Ni.ua.get=function(){return Wi.getUA()},Ni.ua.set=function(i){Wi.setUA(i);var e,t=Wi.getResult();for(e in t)Ni.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.mjs b/dist/ua-parser.pack.mjs index 6c1624036..03863a218 100644 --- a/dist/ua-parser.pack.mjs +++ b/dist/ua-parser.pack.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -function E(i){for(var e={},o=0;o{var o,t={},r=e;if(!_i(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(o in i)t[o]=r[o]&&r[o].length%2==0?r[o].concat(i[o]):i[o];return t})(Ii,e):Ii,M.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Pi(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Pi(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return A(i)&&(r=i.length>B?Ai(i,B):i),this}]]).setUA(r),this):new j(i,e,o).getResult()}j.VERSION="2.0.3",j.BROWSER=E([g,x,C,v]),j.CPU=E([y]),j.DEVICE=E([T,k,v,L,S,i,r,e,F]),j.ENGINE=j.OS=E([g,x]);export{j as UAParser}; \ No newline at end of file +function I(i){for(var e={},o=0;o{var o,t={},r=e;if(!zi(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(o in i)t[o]=r[o]&&r[o].length%2==0?r[o].concat(i[o]):i[o];return t})(Ii,e):Ii,A.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Li(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Li(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>B?Mi(i,B):i),this}]]).setUA(r),this):new V(i,e,o).getResult()}V.VERSION="2.0.4",V.BROWSER=I([g,x,C,v]),V.CPU=I([y]),V.DEVICE=I([S,k,v,G,_,i,r,e,N]),V.ENGINE=V.OS=I([g,x]);export{V as UAParser}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 70cf43945..7dd00eac1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.3", + "version": "2.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.3", + "version": "2.0.4", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index ba1f89d1c..4ca4f0b2e 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.3", + "version": "2.0.4", "author": "Faisal Salman (http://faisalman.com)", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "keywords": [ diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 97c760aa1..e8d36159b 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Enums submodule of UAParser.js v2.0.3 +// Type definitions for Enums submodule of UAParser.js v2.0.4 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 803970c09..174d7873c 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.3 +/* Enums for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 571eb70c5..089af9573 100644 --- a/src/enums/ua-parser-enums.mjs +++ b/src/enums/ua-parser-enums.mjs @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.3 +/* Enums for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -25,7 +25,6 @@ const Browser = Object.freeze({ BAIDU: 'Baidu Browser', BASILISK: 'Basilisk', BLAZER: 'Blazer', - BLU: 'BLU', BOLT: 'Bolt', BOWSER: 'Bowser', BRAVE: 'Brave', @@ -48,6 +47,8 @@ const Browser = Object.freeze({ DUCKDUCKGO: 'DuckDuckGo', ECOSIA: 'Ecosia', EDGE: 'Edge', + EDGE_WEBVIEW: 'Edge WebView', + EDGE_WEBVIEW2: 'Edge WebView2', EPIPHANY: 'Epiphany', FACEBOOK: 'Facebook', FALKON: 'Falkon', @@ -218,6 +219,7 @@ const Vendor = Object.freeze({ ATT: 'AT&T', BENQ: 'BenQ', BLACKBERRY: 'BlackBerry', + BLU: 'BLU', CAT: 'Cat', DELL: 'Dell', ENERGIZER: 'Energizer', @@ -236,6 +238,7 @@ const Vendor = Object.freeze({ ITEL: 'itel', JOLLA: 'Jolla', KOBO: 'Kobo', + LAVA: 'Lava', LENOVO: 'Lenovo', LG: 'LG', MEIZU: 'Meizu', @@ -256,6 +259,7 @@ const Vendor = Object.freeze({ PICO: 'Pico', POLYTRON: 'Polytron', REALME: 'Realme', + RETROID: 'Retroid', RIM: 'RIM', ROKU: 'Roku', SAMSUNG: 'Samsung', @@ -270,6 +274,7 @@ const Vendor = Object.freeze({ TESLA: 'Tesla', ULEFONE: 'Ulefone', VIVO: 'Vivo', + VIZIO: 'Vizio', VODAFONE: 'Vodafone', XBOX: 'Xbox', XIAOMI: 'Xiaomi', @@ -307,6 +312,7 @@ const OS = Object.freeze({ AMIGA_OS: 'Amiga OS', ANDROID: 'Android', ANDROID_X86: 'Android-x86', + ARCAOS: 'ArcaOS', ARCH: 'Arch', BADA: 'Bada', BEOS: 'BeOS', @@ -337,6 +343,7 @@ const OS = Object.freeze({ IOS: 'iOS', JOLI: 'Joli', KAIOS: 'KaiOS', + KNOPPIX: 'Knoppix', KUBUNTU: 'Kubuntu', LINPUS: 'Linpus', LINSPIRE: 'Linspire', @@ -385,10 +392,13 @@ const OS = Object.freeze({ WATCHOS: 'watchOS', WEBOS: 'WebOS', WINDOWS: 'Windows', + WINDOWS_CE: 'Windows CE', WINDOWS_IOT: 'Windows IoT', WINDOWS_MOBILE: 'Windows Mobile', WINDOWS_PHONE: 'Windows Phone', + WINDOWS_RT: 'Windows RT', XBOX: 'Xbox', + XUBUNTU: 'Xubuntu', ZENWALK: 'Zenwalk' // TODO : test! diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 3b9b63a98..4ee31dd86 100644 --- a/src/extensions/ua-parser-extensions.d.ts +++ b/src/extensions/ua-parser-extensions.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.4 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index d89b7ec55..4cd644f11 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.3 +/* Extensions for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 9770ebc54..9401493e4 100644 --- a/src/extensions/ua-parser-extensions.mjs +++ b/src/extensions/ua-parser-extensions.mjs @@ -3,7 +3,7 @@ // Source: /src/extensions/ua-parser-extensions.js /////////////////////////////////////////////// -/* Extensions for UAParser.js v2.0.3 +/* Extensions for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -52,13 +52,15 @@ const Crawlers = Object.freeze({ // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot + // iAskBot - https://iask.ai // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html + // Onespot - https://www.onespot.com/identifying-traffic.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|linkedin|mj12|mojeek|oai-search|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -72,6 +74,9 @@ const Crawlers = Object.freeze({ // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, + // Daum + /(daum(?:oa)?(?:-image)?)[ \/]([\w\.]+)/i, + // Facebook / Meta // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i, @@ -82,6 +87,9 @@ const Crawlers = Object.freeze({ // Internet Archive (archive.org) /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + // Qwantbot - https://help.qwant.com/bot + /(qwantbot)[-\w]*\/?([\w\.]*)/i, + // SemrushBot - http://www.semrush.com/bot.html /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, @@ -97,8 +105,8 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|webzio-extended|(?:screaming frog seo |line|yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -208,9 +216,18 @@ const ExtraDevices = Object.freeze({ const Emails = Object.freeze({ browser : [ [ - // Evolution / Kontact/KMail / [Microsoft/Mac] Outlook / Thunderbird - /(airmail|bluemail|emclient|evolution|foxmail|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|thunderbird|yahoo)(?:m.+ail; |[\/ ])([\w\.]+)/i - ], [NAME, VERSION, [TYPE, EMAIL]] + // Evolution / Kontact/KMail[2] / [Microsoft/Mac] Outlook / Thunderbird + // Airmail / BlueMail / DaumMail / eMClient / Foxmail / NaverMailApp / Polymail + // ProtonMail / SparkDesktop / Sparrow / Yahoo! Mail / Zimbra / ZohoMail-Desktop + /((?:air|blue|daum|fox|poly|proton)mail|emclient|evolution|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|sparkdesktop|thunderbird|yahoo|zohomail-desktop)(?:m.+ail; |[\/ ])([\w\.]+)/i, + + // Apple's Mail + /(mail)\/([\w\.]+) cf/i + ], [NAME, VERSION, [TYPE, EMAIL]], [ + + // Zimbra + /zdesktop\/([\w\.]+)/i + ], [VERSION, [NAME, 'Zimbra'], [TYPE, EMAIL]] ] }); @@ -226,8 +243,11 @@ const Fetchers = Object.freeze({ // DuckAssistBot - https://duckduckgo.com/duckassistbot/ // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan + // Iframely - https://iframely.com/docs/about + // Perplexity-User - https://docs.perplexity.ai/guides/bots + // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|(?:bing|microsoft)preview|chatgpt-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|iframely|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, @@ -261,13 +281,31 @@ const Fetchers = Object.freeze({ /////////////////// const InApps = Object.freeze({ - browser : [ + browser : [[ + // Discord/Figma/Flipboard/Mattermost/Notion/Postman/Rambox/Rocket.Chat/Slack/Teams + /\b(discord|figma|mattermost|notion|postman|rambox|rocket.chat|slack|teams)\/([\w\.]+).+(electron\/|; ios)/i, + /(flipboard)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, INAPP]], [ + + // Evernote/Teams on mobile + /(evernote) win/i, + /(teams)mobile-(ios|and)/i + ], [NAME, [TYPE, INAPP]], [ + // Slack - [/(?:slack(?=.+electron|.+ios)|chatlyio)\/([\d\.]+)/i], - [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], + /chatlyio\/([\d\.]+)/i], + [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], [ + + // TikTok Lite + /ultralite app_version\/([\w\.]+)/i], + [VERSION, [NAME, 'TikTok Lite'], [TYPE, INAPP]], [ + + // VS Code + /\) code\/([\d\.]+).+electron\//i], + [VERSION, [NAME, 'VS Code'], [TYPE, INAPP]], [ // Yahoo! Japan - [/jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], + /jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], [VERSION, [NAME, 'Yahoo! Japan'], [TYPE, INAPP]] ] }); @@ -332,8 +370,10 @@ const Libraries = Object.freeze({ browser : [ // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent [ - /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:urllib|requests)|scrapy)\/([\w\.]+)/i, - /(jsdom|(?<=\()java)\/([\w\.]+)/i + /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i, + /(adobeair|aiohttp|jsdom)\/([\w\.]+)/i, + /(nutch)-([\w\.-]+)(\(|$)/i, + /\((java)\/([\w\.]+)/i ], [NAME, VERSION, [TYPE, LIBRARY]] ] }); diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts index 4b6c80813..fc53c614f 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Helpers submodule of UAParser.js v2.0.3 +// Type definitions for Helpers submodule of UAParser.js v2.0.4 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index d14667b31..8f79fe8f4 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -1,5 +1,5 @@ /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.3 +/* Helpers for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs index e47aae4d8..3530e7dbc 100644 --- a/src/helpers/ua-parser-helpers.mjs +++ b/src/helpers/ua-parser-helpers.mjs @@ -3,7 +3,7 @@ // Source: /src/helpers/ua-parser-helpers.js /////////////////////////////////////////////// -/* Helpers for UAParser.js v2.0.3 +/* Helpers for UAParser.js v2.0.4 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -114,6 +114,9 @@ const isAIBot = (resultOrUA) => [ // You.com 'youbot', + // Zhipu AI + 'chatglm-spider', + // Zyte 'scrapy' diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 6c7c9f8a1..9533b6824 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -1,4 +1,4 @@ -// Type definitions for UAParser.js v2.0.3 +// Type definitions for UAParser.js v2.0.4 // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index e3373d2ed..b325e7b9d 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -19,7 +19,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.3', + var LIBVERSION = '2.0.4', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 00eb9bbcf..13d7caa5c 100644 --- a/src/main/ua-parser.mjs +++ b/src/main/ua-parser.mjs @@ -3,7 +3,7 @@ // Source: /src/main/ua-parser.js ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.3 +/* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License *//* Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. @@ -21,7 +21,7 @@ // Constants ///////////// - var LIBVERSION = '2.0.3', + var LIBVERSION = '2.0.4', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', @@ -154,11 +154,11 @@ has = function (str1, str2) { if (typeof str1 === OBJ_TYPE && str1.length > 0) { for (var i in str1) { - if (lowerize(str1[i]) == lowerize(str2)) return true; + if (lowerize(str2) == lowerize(str1[i])) return true; } return false; } - return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; + return isString(str1) ? lowerize(str2) == lowerize(str1) : false; }, isExtensions = function (obj, deep) { for (var prop in obj) { @@ -249,17 +249,25 @@ // assign given value, ignore regex match this[q[0]] = q[1]; } - } else if (q.length === 3) { - // check whether function or regex + } else if (q.length >= 3) { + // Check whether q[1] FUNCTION or REGEX if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { - // call function (usually string mapper) - this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; + if (q.length > 3) { + this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined; + } else { + // call function (usually string mapper) + this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; + } } else { - // sanitize match using given regex - this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; + if (q.length == 3) { + // sanitize match using given regex + this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; + } else if (q.length == 4) { + this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; + } else if (q.length > 4) { + this[q[0]] = match ? q[3].apply(this, [match.replace(q[1], q[2])].concat(q.slice(4))) : undefined; + } } - } else if (q.length === 4) { - this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; } } else { this[q] = match ? match : undefined; @@ -293,17 +301,17 @@ ////////////// var windowsVersionMap = { - 'ME' : '4.90', - 'NT 3.11' : 'NT3.51', - 'NT 4.0' : 'NT4.0', - '2000' : 'NT 5.0', - 'XP' : ['NT 5.1', 'NT 5.2'], - 'Vista' : 'NT 6.0', - '7' : 'NT 6.1', - '8' : 'NT 6.2', - '8.1' : 'NT 6.3', - '10' : ['NT 6.4', 'NT 10.0'], - 'RT' : 'ARM' + 'ME' : '4.90', + 'NT 3.51': '3.51', + 'NT 4.0': '4.0', + '2000' : ['5.0', '5.01'], + 'XP' : ['5.1', '5.2'], + 'Vista' : '6.0', + '7' : '6.1', + '8' : '6.2', + '8.1' : '6.3', + '10' : ['6.4', '10.0'], + 'NT' : '' }, formFactorsMap = { @@ -315,6 +323,18 @@ 'xr' : ['VR', 'XR'], '?' : ['Desktop', 'Unknown'], '*' : undefined + }, + + browserHintsMap = { + 'Chrome' : 'Google Chrome', + 'Edge' : 'Microsoft Edge', + 'Edge WebView2' : 'Microsoft Edge WebView2', + 'Chrome WebView': 'Android WebView', + 'Chrome Headless':'HeadlessChrome', + 'Huawei Browser': 'HuaweiBrowser', + 'MIUI Browser' : 'Miui Browser', + 'Opera Mobi' : 'OperaMobile', + 'Yandex' : 'YaBrowser' }; ////////////// @@ -328,7 +348,9 @@ // Most common regardless engine /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ - /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge + /webview.+edge\/([\w\.]+)/i // Microsoft Edge + ], [VERSION, [NAME, EDGE+' WebView']], [ + /edg(?:e|ios|a)?\/([\w\.]+)/i ], [VERSION, [NAME, 'Edge']], [ // Presto based @@ -420,7 +442,6 @@ // WebView /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [ - /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp /(daum)apps[\/ ]([\w\.]+)/i, // Daum App @@ -428,7 +449,7 @@ /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /(alipay)client\/([\w\.]+)/i, // Alipay /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter - /(instagram|snapchat)[\/ ]([-\w\.]+)/i // Instagram/Snapchat + /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna ], [NAME, VERSION, [TYPE, INAPP]], [ /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [ @@ -443,6 +464,9 @@ /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless ], [VERSION, [NAME, CHROME+' Headless']], [ + /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2 + ], [VERSION, [NAME, EDGE+' WebView2']], [ + / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView ], [[NAME, CHROME+' WebView'], VERSION], [ @@ -535,7 +559,7 @@ /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [ /\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, - /samsung[- ]((?!sm-[lr])[-\w]+)/i, + /samsung[- ]((?!sm-[lr]|browser)[-\w]+)/i, /sec-(sgh\w+)/i ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ @@ -580,16 +604,22 @@ / ([\w ]+) miui\/v?\d/i ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + // OnePlus + /droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i, + /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ + // OPPO /; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i ], [MODEL, [VENDOR, OPPO], [TYPE, MOBILE]], [ /\b(opd2(\d{3}a?))(?: bui|\))/i - ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [ + ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [ + + // BLU + /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i // Vivo series + ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ - // BLU Vivo Series - /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i - ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ // Vivo /; vivo (\w+)(?: bui|\))/i, /\b(v[12]\d{3}\w?[at])(?: bui|;)/i @@ -599,10 +629,17 @@ /\b(rmx[1-3]\d{3})(?: bui|;|\))/i ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ + // Lenovo + /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, + /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ + /lenovo[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, MOBILE]], [ + // Motorola /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, - /\bmot(?:orola)?[- ](\w*)/i, - /((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i + /\bmot(?:orola)?[- ]([\w\s]+)(\)| bui)/i, + /((?:moto(?! 360)[-\w\(\) ]+|xt\d{3,4}[cgkosw\+]?[-\d]*|nexus 6)(?= bui|\)))/i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ /\b(mz60\d|xoom[2 ]{0,2}) build\//i ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ @@ -611,15 +648,10 @@ /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, - /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch))(\w+)/i, + /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i, /\blg-?([\d\w]+) bui/i ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ - // Lenovo - /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, - /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i - ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ - // Nokia /(nokia) (t[12][01])/i ], [VENDOR, MODEL, [TYPE, TABLET]], [ @@ -630,21 +662,19 @@ // Google /(pixel (c|tablet))\b/i // Google Pixel C/Tablet ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ - /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel + // Google Pixel + /droid.+;(?: google)? (g(01[13]a|020[aem]|025[jn]|1b60|1f8f|2ybb|4s1m|576d|5nz6|8hhn|8vou|a02099|c15s|d1yq|e2ae|ec77|gh2x|kv4x|p4bc|pj41|r83y|tt9q|ur25|wvk6)|pixel[\d ]*a?( pro)?( xl)?( fold)?( \(5g\))?)( bui|\))/i ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ + /(google) (pixelbook( go)?)/i + ], [VENDOR, MODEL], [ // Sony - /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i + /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [ /sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [ - // OnePlus - / (kb2005|in20[12]5|be20[12][59])\b/i, - /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i - ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ - // Amazon /(alexa)webm/i, /(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show @@ -715,7 +745,7 @@ ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [ // Nothing - /droid.+; (a(?:015|06[35]|142p?))/i + /droid.+; (a(in)?(0(15|59|6[35])|142)p?)/i ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ // Archos @@ -726,17 +756,20 @@ /; (ac[3-6]\d\w{2,8})( b|\))/i ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [ + // HMD + /; (n159v)/i + ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [ + // MIXED /(imo) (tab \w+)/i, // IMO - /(infinix) (x1101b?)/i // Infinix XPad + /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno ], [VENDOR, MODEL, [TYPE, TABLET]], [ - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i, - // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan - /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan + /; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia - /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo /(oppo) ?([\w ]+) bui/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ @@ -768,6 +801,7 @@ ], [VENDOR, [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(vizio)(?: |.+model\/)(\w+-\w+)/i, // Vizio /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i @@ -799,26 +833,27 @@ ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices - ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [ + ], [[VENDOR, /.+\/(\w+)/, '$1', strMapper, {'LG':'lge'}], [MODEL, trim], [TYPE, SMARTTV]], [ // SmartTV from Unidentified Vendors /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i ], [MODEL, [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:|large screen[\w ]+safari)\b/i ], [[TYPE, SMARTTV]], [ /////////////////// // CONSOLES /////////////////// - /(ouya)/i, // Ouya - /(nintendo) (\w+)/i // Nintendo - ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ - /droid.+; (shield)( bui|\))/i // Nvidia Portable - ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ /(playstation \w+)/i // Playstation ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [ /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [ + /(ouya)/i, // Ouya + /(nintendo) (\w+)/i, // Nintendo + /(retroid) (pocket ([^\)]+))/i // Retroid Pocket + ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ + /droid.+; (shield)( bui|\))/i // Nvidia Portable + ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ /////////////////// // WEARABLES @@ -854,6 +889,8 @@ ], [VENDOR, MODEL, [TYPE, XR]], [ /(quest( \d| pro)?s?).+vr/i // Meta Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ + /mobile vr; rv.+firefox/i // Unidentifiable VR device using Firefox Reality / Wolvic + ], [[TYPE, XR]], [ /////////////////// // EMBEDDED @@ -865,7 +902,7 @@ ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ /(homepod).+mac os/i // Apple HomePod ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [ - /windows iot/i + /windows iot/i // Unidentifiable embedded device using Windows IoT ], [[TYPE, EMBEDDED]], [ //////////////////// @@ -911,14 +948,17 @@ os : [[ // Windows - /microsoft (windows) (vista|xp)/i // Windows (iTunes) + /(windows nt) (6\.[23]); arm/i // Windows RT + ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [ + /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone + // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11 + /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|server 20\d\d( r2)?|vista|xp)/i + ], [NAME, VERSION], [ + /windows nt ?([\d\.\)]*)(?!.+xbox)/i, + /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i + ], [[VERSION, /(;|\))/g, '', strMapper, windowsVersionMap], [NAME, WINDOWS]], [ + /(windows ce)\/?([\d\.]*)/i // Windows CE ], [NAME, VERSION], [ - /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone - ], [NAME, [VERSION, strMapper, windowsVersionMap]], [ - /windows nt 6\.2; (arm)/i, // Windows RT - /windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i, - /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i - ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [ // iOS/macOS /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS @@ -926,7 +966,7 @@ /cfnetwork\/.+darwin/i ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ /(mac os x) ?([\w\. ]*)/i, - /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS + /(macintosh|mac_powerpc\b)(?!.+(haiku|morphos))/i // Mac OS ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ // Google Chromecast @@ -942,12 +982,13 @@ ], [VERSION, [NAME, CHROMECAST]], [ // Mobile OSes - /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS + /droid ([\w\.]+)\b.+(android[- ]x86)/i // Android-x86 ], [VERSION, NAME], [ /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ + /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen - /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen)\w*[-\/\.; ]?([\d\.]*)/i ], [NAME, VERSION], [ /\(bb(10);/i // BlackBerry 10 ], [VERSION, [NAME, BLACKBERRY]], [ @@ -955,9 +996,12 @@ ], [VERSION, [NAME, 'Symbian']], [ /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS ], [VERSION, [NAME, FIREFOX+' OS']], [ - /web0s;.+rt(tv)/i, - /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS + /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS + /webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i ], [VERSION, [NAME, 'webOS']], [ + /web0s;.+?(?:chr[o0]me|safari)\/(\d+)/i + // https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine + ], [[VERSION, strMapper, {'25':'120','24':'108','23':'94','22':'87','6':'79','5':'68','4':'53','3':'38','2':'538','1':'537','*':'TV'}], [NAME, 'webOS']], [ /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS ], [VERSION, [NAME, 'watchOS']], [ @@ -977,20 +1021,20 @@ // Other /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm - /(mint)[\/\(\) ]?(\w*)/i, // Mint - /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux - /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, - // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire - /(hurd|linux)(?: arm\w*| x86\w*| ?)([\w\.]*)/i, // Hurd/Linux + /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint + /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire|knoppix)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire/Knoppix + /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris + /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX + /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS /(gnu) ?([\w\.]*)/i, // GNU /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly - /(haiku) (\w+)/i // Haiku + /(haiku) ?(r\d)?/i // Haiku ], [NAME, VERSION], [ - /(sunos) ?([\w\.\d]*)/i // Solaris + /(sunos) ?([\d\.]*)/i // Solaris ], [[NAME, 'Solaris'], VERSION], [ - /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris - /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX - /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS + /\b(beos|os\/2|amigaos|openvms|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/HP-UX/SerenityOS /(unix) ?([\w\.]*)/i // UNIX ], [NAME, VERSION] ] @@ -1224,21 +1268,20 @@ for (var i in brands) { var brandName = brands[i].brand || brands[i], brandVersion = brands[i].version; - if (this.itemType == UA_BROWSER && !/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && brandName != CHROMIUM))) { - brandName = strMapper(brandName, { - 'Chrome' : 'Google Chrome', - 'Edge' : 'Microsoft Edge', - 'Chrome WebView' : 'Android WebView', - 'Chrome Headless' : 'HeadlessChrome', - 'Huawei Browser' : 'HuaweiBrowser', - 'MIUI Browser' : 'Miui Browser', - 'Opera Mobi' : 'OperaMobile', - 'Yandex' : 'YaBrowser' - }); - this.set(NAME, brandName) - .set(VERSION, brandVersion) - .set(MAJOR, majorize(brandVersion)); - prevName = brandName; + if (this.itemType == UA_BROWSER && + !/not.a.brand/i.test(brandName) && + (!prevName || + (/Chrom/.test(prevName) && brandName != CHROMIUM) || + (prevName == EDGE && /WebView2/.test(brandName)) + )) { + brandName = strMapper(brandName, browserHintsMap); + prevName = this.get(NAME); + if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) { + this.set(NAME, brandName) + .set(VERSION, brandVersion) + .set(MAJOR, majorize(brandVersion)); + } + prevName = brandName; } if (this.itemType == UA_ENGINE && brandName == CHROMIUM) { this.set(VERSION, brandVersion); From 088383b9bd75003b9aeb1b67b5d2f4eabd8bac03 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 21 Jul 2025 11:43:26 +0700 Subject: [PATCH 377/388] Add new CPU architecture: alpha --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 12 ++++++------ test/data/ua/cpu/cpu-all.json | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index e8d36159b..a8070fe1e 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -166,6 +166,7 @@ export const BrowserType: Readonly<{ }>; export const CPU: Readonly<{ '68K': "68k"; + ALPHA: "alpha"; ARM: "arm"; ARM_64: "arm64"; ARM_HF: "armhf"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 174d7873c..ecedb6d02 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -175,6 +175,7 @@ const BrowserType = Object.freeze({ const CPU = Object.freeze({ '68K': '68k', + ALPHA: 'alpha', ARM : 'arm', ARM_64: 'arm64', ARM_HF: 'armhf', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b325e7b9d..7d8c28cd3 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -536,15 +536,15 @@ /( (ce|mobile); ppc;|\/[\w\.]+arm\b)/i ], [[ARCHITECTURE, 'arm']], [ - /((ppc|powerpc)(64)?)( mac|;|\))/i // PowerPC - ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ - / sun4\w[;\)]/i // SPARC ], [[ARCHITECTURE, 'sparc']], [ - - /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC - ], [[ARCHITECTURE, lowerize]] + /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i, + /((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC + /(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha + ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ + /winnt.+\[axp/i + ], [[ARCHITECTURE, 'alpha']] ], device : [[ diff --git a/test/data/ua/cpu/cpu-all.json b/test/data/ua/cpu/cpu-all.json index a4f68b1b6..e9b749e7d 100644 --- a/test/data/ua/cpu/cpu-all.json +++ b/test/data/ua/cpu/cpu-all.json @@ -87,6 +87,22 @@ "architecture" : "amd64" } }, + { + "desc" : "Alpha", + "ua" : "Mozilla/3.01 (WinNT; I) [AXP]", + "expect" : + { + "architecture" : "alpha" + } + }, + { + "desc" : "Alpha", + "ua" : "Mozilla/5.0 (X11; OpenBSD alpha; rv:78.0) Gecko/20100101 Firefox/78.0", + "expect" : + { + "architecture" : "alpha" + } + }, { "desc" : "ARM", "ua" : "Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537", From a19977ce4c99a03e7b89c6c8d5b579bd4c6370ce Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 21 Jul 2025 11:53:03 +0700 Subject: [PATCH 378/388] Fix #796: Improve device detection for Pico Neo 3 --- src/main/ua-parser.js | 2 +- test/data/ua/device/pico.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 7d8c28cd3..0d5eb3db3 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -883,7 +883,7 @@ /droid.+; (glass) \d/i // Google Glass ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ - /(pico) (4|neo3(?: link|pro)?)/i // Pico + /(pico) ([\w ]+) os\d/i // Pico ], [VENDOR, MODEL, [TYPE, XR]], [ /(quest( \d| pro)?s?).+vr/i // Meta Quest ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ diff --git a/test/data/ua/device/pico.json b/test/data/ua/device/pico.json index 2f2821232..029a7c153 100644 --- a/test/data/ua/device/pico.json +++ b/test/data/ua/device/pico.json @@ -25,5 +25,23 @@ "model": "Neo3 Link", "type": "xr" } + }, + { + "desc": "Pico Neo 3", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo 3 OS5.12.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.48 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect": { + "vendor": "Pico", + "model": "Neo 3", + "type": "xr" + } + }, + { + "desc": "Pico Neo 3 Pro", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo 3 Pro OS5.9.9.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.46 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect": { + "vendor": "Pico", + "model": "Neo 3 Pro", + "type": "xr" + } } ] \ No newline at end of file From 0bb6e24837aec7aec10d8cd1c2491128e058e21a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 22 Jul 2025 12:29:01 +0700 Subject: [PATCH 379/388] [extensions] Add new bots: Blueno, BufferLinkPreviewBot, Claude-SearchBot, Claude-User, Coveobot, CriteoBot --- src/extensions/ua-parser-extensions.js | 13 +++++++----- src/helpers/ua-parser-helpers.js | 4 ++++ test/data/ua/extension/crawler.json | 28 ++++++++++++++++++++++---- test/data/ua/extension/fetcher.json | 20 ++++++++++++++++++ 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 4cd644f11..06ef90ff0 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -44,6 +44,8 @@ const Crawlers = Object.freeze({ // Amazonbot - https://developer.amazon.com/amazonbot // Bingbot / AdIdxBot - https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0 // CCBot - https://commoncrawl.org/faq + // Coveobot - https://connect.coveo.com/s/article/19648 + // CriteoBot - https://www.criteo.com/criteo-crawler/ // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot // DuckDuckBot - http://duckduckgo.com/duckduckbot.html // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ @@ -56,7 +58,7 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|coveo|criteo|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -65,7 +67,7 @@ const Crawlers = Object.freeze({ /(baiduspider[-imagevdonwsfcpr]{0,7})\/?([\w\.]*)/i, // ClaudeBot (Anthropic) - /(claude(?:bot|-web)|anthropic-ai)\/?([\w\.]*)/i, + /(claude(?:bot|-searchbot|-web)|anthropic-ai)\/?([\w\.]*)/i, // Coc Coc Bot - https://help.coccoc.com/en/search-engine /(coccocbot-(?:image|web))\/([\w\.]+)/i, @@ -235,15 +237,16 @@ const Fetchers = Object.freeze({ browser : [ [ // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit + // Buffer Link Preview Bot - https://scraper.buffer.com/about/bots/link-preview-bot // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // DuckAssistBot - https://duckduckgo.com/duckassistbot/ - // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot + // Better Uptime / BingPreview / Blueno / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan // Iframely - https://iframely.com/docs/about // Perplexity-User - https://docs.perplexity.ai/guides/bots // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|iframely|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|mastodon|(?:bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|iframely|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, @@ -404,8 +407,8 @@ const Vehicles = Object.freeze({ const Bots = Object.freeze({ browser : [ ...CLIs.browser, - ...Crawlers.browser, ...Fetchers.browser, + ...Crawlers.browser, ...Libraries.browser ], os : [ diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 8f79fe8f4..c00e49b68 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -52,6 +52,7 @@ const isAIBot = (resultOrUA) => [ // Anthropic 'anthropic-ai', 'claude-web', + 'claude-searchbot', 'claudebot', // Apple @@ -63,6 +64,9 @@ const isAIBot = (resultOrUA) => [ // Common Crawl 'ccbot', + + // Coveo + 'coveobot', // DataForSeo 'dataforseobot', diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 918ee9862..ac580fc83 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -269,6 +269,16 @@ "type" : "crawler" } }, + { + "desc" : "ClaudeWeb", + "ua" : "Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)", + "expect" : + { + "name" : "Claude-Web", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Coc Coc Bot (web)", "ua" : "Mozilla/5.0 (compatible; coccocbot-web/1.0; +http://help.coccoc.com/searchengine)", @@ -290,12 +300,22 @@ } }, { - "desc" : "ClaudeWeb", - "ua" : "Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)", + "desc" : "Coveobot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) (compatible; Coveobot/2.0;+http://www.coveo.com/bot.html)", "expect" : { - "name" : "Claude-Web", - "version" : "1.0", + "name" : "Coveobot", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "CriteoBot", + "ua" : "CriteoBot/0.1 (+https://www.criteo.com/criteo-crawler/)", + "expect" : + { + "name" : "CriteoBot", + "version" : "0.1", "type" : "crawler" } }, diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 4a053939a..19b05bba0 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -29,6 +29,16 @@ "type" : "fetcher" } }, + { + "desc" : "Blueno", + "ua" : "acebookexternalhit/1.1 (compatible; Blueno/1.0; +http://naver.me/scrap)", + "expect" : + { + "name" : "Blueno", + "version" : "1.0", + "type" : "fetcher" + } + }, { "desc" : "Bluesky", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Bluesky Cardyb/1.1; +mailto:support@bsky.app) Chrome/100.0.0.0 Safari/537.36", @@ -39,6 +49,16 @@ "type" : "fetcher" } }, + { + "desc" : "BufferLinkPreviewBot", + "ua" : "BufferLinkPreviewBot/1.0 (+https://scraper.buffer.com/about/bots/link-preview-bot)", + "expect" : + { + "name" : "BufferLinkPreviewBot", + "version" : "1.0", + "type" : "fetcher" + } + }, { "desc" : "ChatGPT-User", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot", From 3fe137e533cb79dd24ba47dccf31c102ee316e54 Mon Sep 17 00:00:00 2001 From: undefined Date: Fri, 1 Aug 2025 19:33:10 +0800 Subject: [PATCH 380/388] chore: move node-fetch to devDeps (#784) --- package-lock.json | 10 +++++++--- package.json | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7dd00eac1..8fb1f5e97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,6 @@ "@types/node-fetch": "^2.6.12", "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", - "node-fetch": "^2.7.0", "ua-is-frozen": "^0.1.2" }, "bin": { @@ -38,6 +37,7 @@ "@playwright/test": "^1.49.0", "jshint": "~2.13.6", "mocha": "~8.2.0", + "node-fetch": "^2.7.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", @@ -1967,6 +1967,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2716,7 +2717,8 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true }, "node_modules/trim-newlines": { "version": "3.0.1", @@ -2809,12 +2811,14 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/package.json b/package.json index 4ca4f0b2e..b39f0b008 100755 --- a/package.json +++ b/package.json @@ -227,7 +227,6 @@ "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", "ua-is-frozen": "^0.1.2", - "node-fetch": "^2.7.0", "@types/node-fetch": "^2.6.12" }, "devDependencies": { @@ -236,6 +235,7 @@ "@playwright/test": "^1.49.0", "jshint": "~2.13.6", "mocha": "~8.2.0", + "node-fetch": "^2.7.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", From ab299a23b7bc75d50ab0b02205809cca274ba63d Mon Sep 17 00:00:00 2001 From: Suryaansh Chawla Date: Fri, 1 Aug 2025 17:03:52 +0530 Subject: [PATCH 381/388] Zalo integration in UAParser (#1) (#792) --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 3 ++- src/main/ua-parser.js | 2 ++ test/data/ua/browser/browser-all.json | 22 ++++++++++++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index a8070fe1e..933bccb22 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -154,6 +154,7 @@ export const Browser: Readonly<{ WHALE: "Whale"; WOLVIC: "Wolvic"; YANDEX: "Yandex"; + ZALO: "Zalo"; }>; export const BrowserType: Readonly<{ CRAWLER: "crawler"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index ecedb6d02..828e9e8ef 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -158,7 +158,8 @@ const Browser = Object.freeze({ WEIBO: 'Weibo', WHALE: 'Whale', WOLVIC: 'Wolvic', - YANDEX: 'Yandex' + YANDEX: 'Yandex', + ZALO: 'Zalo' // TODO : test! }); diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 0d5eb3db3..4051869ea 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -455,6 +455,8 @@ ], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [ /\[(linkedin)app\]/i // LinkedIn App for iOS & Android ], [NAME, [TYPE, INAPP]], [ + /(zalo(?:app)?)[\/\sa-z]*([\w\.-]+)/i // Zalo + ], [[NAME, /(.+)/, 'Zalo'], VERSION, [TYPE, INAPP]], [ /(chromium)[\/ ]([-\w\.]+)/i // Chromium ], [NAME, VERSION], [ diff --git a/test/data/ua/browser/browser-all.json b/test/data/ua/browser/browser-all.json index 9f39de901..a2a637903 100644 --- a/test/data/ua/browser/browser-all.json +++ b/test/data/ua/browser/browser-all.json @@ -2695,5 +2695,27 @@ "major" : "10", "type" : "inapp" } + }, + { + "desc" : "Zalo on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Zalo/20.05.01 Mobile/15E148", + "expect" : + { + "name" : "Zalo", + "version" : "20.05.01", + "major" : "20", + "type" : "inapp" + } + }, + { + "desc" : "Zalo on Android", + "ua" : "Mozilla/5.0 (Linux; Android 10; Vsmart Live Build/QKQ1.190918.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36 Zalo/20.04.02.r1", + "expect" : + { + "name" : "Zalo", + "version" : "20.04.02.r1", + "major" : "20", + "type" : "inapp" + } } ] \ No newline at end of file From ecbc0336b644604b8483530ba0daacafd96cbdc3 Mon Sep 17 00:00:00 2001 From: Aidan Nulman Date: Fri, 1 Aug 2025 07:34:36 -0400 Subject: [PATCH 382/388] Fix #797: Iterate over `brands` as an array (#798) --- dist/ua-parser.min.js | 2 +- dist/ua-parser.min.mjs | 2 +- dist/ua-parser.pack.js | 2 +- dist/ua-parser.pack.mjs | 2 +- src/main/ua-parser.js | 4 +++- src/main/ua-parser.mjs | 4 +++- test/unit/main.js | 30 +++++++++++++++++++++++++++++- 7 files changed, 39 insertions(+), 7 deletions(-) diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 7d65516a5..22782dfc5 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -(function(window,undefined){"use strict";var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){this[q[0]]=match?match.replace(q[1],q[2]):undefined}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}else if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/Chrom/.test(prevName)&&brandName!=CHROMIUM||prevName==EDGE&&/WebView2/.test(brandName))){brandName=strMapper(brandName,browserHintsMap);prevName=this.get(NAME);if(!(prevName&&!/Chrom/.test(prevName)&&/Chrom/.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +(function(window,undefined){"use strict";var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){if(!arr.hasOwnProperty(i))continue;var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){this[q[0]]=match?match.replace(q[1],q[2]):undefined}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}else if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i=0;i=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.min.mjs b/dist/ua-parser.min.mjs index 3cb08310a..c8afed4a9 100644 --- a/dist/ua-parser.min.mjs +++ b/dist/ua-parser.min.mjs @@ -1,4 +1,4 @@ /* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){this[q[0]]=match?match.replace(q[1],q[2]):undefined}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}else if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i in brands){var brandName=brands[i].brand||brands[i],brandVersion=brands[i].version;if(this.itemType==UA_BROWSER&&!/not.a.brand/i.test(brandName)&&(!prevName||/Chrom/.test(prevName)&&brandName!=CHROMIUM||prevName==EDGE&&/WebView2/.test(brandName))){brandName=strMapper(brandName,browserHintsMap);prevName=this.get(NAME);if(!(prevName&&!/Chrom/.test(prevName)&&/Chrom/.test(brandName))){this.set(NAME,brandName).set(VERSION,brandVersion).set(MAJOR,majorize(brandVersion))}prevName=brandName}if(this.itemType==UA_ENGINE&&brandName==CHROMIUM){this.set(VERSION,brandVersion)}}}break;case UA_CPU:var archName=uaCH[ARCHITECTURE];if(archName){if(archName&&uaCH[BITNESS]=="64")archName+="64";rgxMapper.call(this.data,archName+";",rgxMap)}break;case UA_DEVICE:if(uaCH[MOBILE]){this.set(TYPE,MOBILE)}if(uaCH[MODEL]){this.set(MODEL,uaCH[MODEL]);if(!this.get(TYPE)||!this.get(VENDOR)){var reParse={};rgxMapper.call(reParse,"droid 9; "+uaCH[MODEL]+")",rgxMap);if(!this.get(TYPE)&&!!reParse.type){this.set(TYPE,reParse.type)}if(!this.get(VENDOR)&&!!reParse.vendor){this.set(VENDOR,reParse.vendor)}}}if(uaCH[FORMFACTORS]){var ff;if(typeof uaCH[FORMFACTORS]!=="string"){var idx=0;while(!ff&&idx=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file +var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;if(!isExtensions(extensions)){extraRgx={};for(var i in extensions){for(var j in extensions[i]){extraRgx[j]=extensions[i][j].concat(extraRgx[j]?extraRgx[j]:[])}}}for(var k in defaultRgx){mergedRgx[k]=extraRgx[k]&&extraRgx[k].length%2===0?extraRgx[k].concat(defaultRgx[k]):defaultRgx[k]}return mergedRgx},enumerize=function(arr){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){if(!arr.hasOwnProperty(i))continue;var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){this[q[0]]=match?match.replace(q[1],q[2]):undefined}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}else if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i=0;i=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 94f5cc53d..fec42a72e 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ /* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -((i,l)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!Ti(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Pi,e):Pi,M.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Gi(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Gi(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return O(i)&&(r=i.length>U?Mi(i,U):i),this}]]).setUA(r),this):new V(i,e,t).getResult()}V.VERSION="2.0.4",V.BROWSER=I([v,y,G,k]),V.CPU=I([C]),V.DEVICE=I([S,x,k,W,_,e,r,t,F]),V.ENGINE=V.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=V:exports).UAParser=V:typeof define===R&&define.amd?define(function(){return V}):qi&&(i.UAParser=V);var Wi,Ni=qi&&(i.jQuery||i.Zepto);Ni&&!Ni.ua&&(Wi=new V,Ni.ua=Wi.getResult(),Ni.ua.get=function(){return Wi.getUA()},Ni.ua.set=function(i){Wi.setUA(i);var e,t=Wi.getResult();for(e in t)Ni.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file +((i,c)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!Ti(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ui,e):Ui,M.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Gi(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Gi(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>P?Mi(i,P):i),this}]]).setUA(r),this):new V(i,e,t).getResult()}V.VERSION="2.0.4",V.BROWSER=I([v,y,G,k]),V.CPU=I([C]),V.DEVICE=I([S,x,k,W,_,e,r,t,F]),V.ENGINE=V.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=V:exports).UAParser=V:typeof define===R&&define.amd?define(function(){return V}):qi&&(i.UAParser=V);var Wi,Ni=qi&&(i.jQuery||i.Zepto);Ni&&!Ni.ua&&(Wi=new V,Ni.ua=Wi.getResult(),Ni.ua.get=function(){return Wi.getUA()},Ni.ua.set=function(i){Wi.setUA(i);var e,t=Wi.getResult();for(e in t)Ni.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.mjs b/dist/ua-parser.pack.mjs index 03863a218..8ab1e0ee1 100644 --- a/dist/ua-parser.pack.mjs +++ b/dist/ua-parser.pack.mjs @@ -1,4 +1,4 @@ /* UAParser.js v2.0.4 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -function I(i){for(var e={},o=0;o{var o,t={},r=e;if(!zi(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(o in i)t[o]=r[o]&&r[o].length%2==0?r[o].concat(i[o]):i[o];return t})(Ii,e):Ii,A.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Li(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(u,this.getCPU()).set(h,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Li(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(u)],["getDevice",n(h)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>B?Mi(i,B):i),this}]]).setUA(r),this):new V(i,e,o).getResult()}V.VERSION="2.0.4",V.BROWSER=I([g,x,C,v]),V.CPU=I([y]),V.DEVICE=I([S,k,v,G,_,i,r,e,N]),V.ENGINE=V.OS=I([g,x]);export{V as UAParser}; \ No newline at end of file +function I(i){for(var e={},o=0;o{var o,t={},r=e;if(!zi(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(o in i)t[o]=r[o]&&r[o].length%2==0?r[o].concat(i[o]):i[o];return t})(Ii,e):Ii,A.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Li(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(h,this.getCPU()).set(u,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Li(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(h)],["getDevice",n(u)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>P?Mi(i,P):i),this}]]).setUA(r),this):new V(i,e,o).getResult()}V.VERSION="2.0.4",V.BROWSER=I([g,x,C,v]),V.CPU=I([y]),V.DEVICE=I([S,k,v,G,_,i,r,e,N]),V.ENGINE=V.OS=I([g,x]);export{V as UAParser}; \ No newline at end of file diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 4051869ea..b7edcf15e 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -188,6 +188,8 @@ }, setProps = function (arr) { for (var i in arr) { + if (!arr.hasOwnProperty(i)) continue; + var propName = arr[i]; if (typeof propName == OBJ_TYPE && propName.length == 2) { this[propName[0]] = propName[1]; @@ -1265,7 +1267,7 @@ case UA_ENGINE: var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; if (brands) { - for (var i in brands) { + for (var i=0; i new UAParser('').getResult()); + + function withMangledArrayProto(fn, key = 'isEmpty', value = function() { return this.length === 0; }) { + const originalValue = Array.prototype[key]; + const restore = Object.hasOwnProperty.call(Array.prototype, key) + ? () => Array.prototype[key] = originalValue + : () => delete Array.prototype[key]; + + Array.prototype[key] = value; + const result = fn(); + restore(); + + return result; + } + + assert.deepEqual(result, + { + ua : '', + browser: { name: undefined, version: undefined, major: undefined, type: undefined }, + cpu: { architecture: undefined }, + device: { vendor: undefined, model: undefined, type: undefined }, + engine: { name: undefined, version: undefined}, + os: { name: undefined, version: undefined } + }); + done(); + }); }); describe('Extending Regex', function () { @@ -353,4 +381,4 @@ describe('Read user-agent data from req.headers', function () { const { browser } = UAParser(reqHeaders); assert.strictEqual(browser.is('Midori'), true); }); -}); \ No newline at end of file +}); From bf5155ec8a0d5d231b975bb04b05cfdd130877c6 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 9 Aug 2025 23:01:29 +0700 Subject: [PATCH 383/388] Add new vendor: Philips --- src/enums/ua-parser-enums.d.ts | 1 + src/enums/ua-parser-enums.js | 1 + src/main/ua-parser.js | 13 ++--- test/data/ua/device/_others.json | 36 -------------- test/data/ua/device/philips.json | 83 ++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 test/data/ua/device/philips.json diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 933bccb22..aa40b50f6 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -243,6 +243,7 @@ export const Vendor: Readonly<{ PALM: "Palm"; PANASONIC: "Panasonic"; PEBBLE: "Pebble"; + PHILIPS: "Philips"; PICO: "Pico"; POLYTRON: "Polytron"; REALME: "Realme"; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 828e9e8ef..9aa28e6c3 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -254,6 +254,7 @@ const Vendor = Object.freeze({ PALM: 'Palm', PANASONIC: 'Panasonic', PEBBLE: 'Pebble', + PHILIPS: 'Philips', PICO: 'Pico', POLYTRON: 'Polytron', REALME: 'Realme', diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index b7edcf15e..5f2d042b8 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -772,7 +772,8 @@ /; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL /(hp) ([\w ]+\w)/i, // HP iPAQ /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia - /(oppo) ?([\w ]+) bui/i // OPPO + /(oppo) ?([\w ]+) bui/i, // OPPO + /droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /(kobo)\s(ereader|touch)/i, // Kobo @@ -799,6 +800,7 @@ // SMARTTVS /////////////////// + /(philips)[\w ]+tv/i, // Philips /smart-tv.+(samsung)/i // Samsung ], [VENDOR, [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i @@ -836,11 +838,6 @@ /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices ], [[VENDOR, /.+\/(\w+)/, '$1', strMapper, {'LG':'lge'}], [MODEL, trim], [TYPE, SMARTTV]], [ - // SmartTV from Unidentified Vendors - /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i - ], [MODEL, [TYPE, SMARTTV]], [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:|large screen[\w ]+safari)\b/i - ], [[TYPE, SMARTTV]], [ /////////////////// // CONSOLES @@ -911,6 +908,10 @@ // MIXED (GENERIC) /////////////////// + /droid.+; ([\w- ]+) (4k|android|smart|google)[- ]?tv/i // Unidentifiable SmartTV + ], [MODEL, [TYPE, SMARTTV]], [ + /\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i + ], [[TYPE, SMARTTV]], [ /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i ], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [ /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json index 55329b3e3..06a70fef7 100644 --- a/test/data/ua/device/_others.json +++ b/test/data/ua/device/_others.json @@ -122,42 +122,6 @@ "type": "undefined" } }, - { - "desc": "Philips SmartTV", - "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", - "expect": { - "vendor": "Philips", - "model": "", - "type": "smarttv" - } - }, - { - "desc": "Philips 32PFL6606K/02 SmartTV (2011)", - "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", - "expect": { - "vendor": "Philips", - "model": "", - "type": "smarttv" - } - }, - { - "desc": "Philips 32PFL6606K/02 SmartTV (2013)", - "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", - "expect": { - "vendor": "Philips", - "model": "", - "type": "smarttv" - } - }, - { - "desc": "Philips 32PHS5301/12 SmartTV (2016)", - "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36 OPR/29.0.1803.0 OMI/4.5.23.37.MOT2.13 HbbTV/1.2.1 (;Philips;32PHS5301/12;;_TV_MT5800;) Firmware/TPM161E_012.002.045.001 en", - "expect": { - "vendor": "Philips", - "model": "32PHS5301/12", - "type": "smarttv" - } - }, { "desc": "Samsung SmartTV", "ua": "Mozilla/5.0 (SMART-TV; X11; Linux armv7l) AppleWebkit/537.42 (KHTML, like Gecko) Safari/537.42", diff --git a/test/data/ua/device/philips.json b/test/data/ua/device/philips.json new file mode 100644 index 000000000..ebc5af032 --- /dev/null +++ b/test/data/ua/device/philips.json @@ -0,0 +1,83 @@ +[ + { + "desc": "Philips S616", + "ua": "Mozilla/5.0 (Linux; Android 5.1; Philips S616 Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36", + "expect": { + "vendor": "Philips", + "model": "S616", + "type": "mobile" + } + }, + { + "desc": "Philips W8510", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Philips W8510 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.68 Mobile Safari/537.36", + "expect": { + "vendor": "Philips", + "model": "W8510", + "type": "mobile" + } + }, + { + "desc": "Philips SmartTV", + "ua": "Mozilla/5.0 (Linux; Android 11; PHILIPS 4k TV Build/RTXC.231010.082.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.71 Mobile Safari/537.36", + "expect": { + "vendor": "PHILIPS", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Philips SmartTV", + "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PFL6606K/02 SmartTV (2011)", + "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PFL6606K/02 SmartTV (2013)", + "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PHS5301/12 SmartTV (2016)", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36 OPR/29.0.1803.0 OMI/4.5.23.37.MOT2.13 HbbTV/1.2.1 (;Philips;32PHS5301/12;;_TV_MT5800;) Firmware/TPM161E_012.002.045.001 en", + "expect": { + "vendor": "Philips", + "model": "32PHS5301/12", + "type": "smarttv" + } + }, + { + "desc": "Philips PH0M_EA_T32", + "ua": "Mozilla/5.0 (Linux; Android 10; Philips FHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.186 Mobile Safari/537.36", + "expect": { + "vendor": "Philips", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Philips PH3M_AL_T32", + "ua": "Mozilla/5.0 (Linux; Android 11; Philips Google TV TA7 Build/RTM5.220609.199; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.58 Mobile Safari/537.36", + "expect": { + "vendor": "Philips", + "model": "undefined", + "type": "smarttv" + } + } +] \ No newline at end of file From 9e6dff6dc3cce99816a3ddac4b0cecbb06c22f5f Mon Sep 17 00:00:00 2001 From: Harlan Brawer Date: Mon, 28 Jul 2025 15:31:51 -0700 Subject: [PATCH 384/388] replace node fetch types with undici --- package-lock.json | 90 +++++------------------------------------ package.json | 3 +- src/main/ua-parser.d.ts | 6 +-- test/unit/main.js | 2 +- 4 files changed, 15 insertions(+), 86 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8fb1f5e97..1265e3dd9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,10 +23,10 @@ ], "license": "AGPL-3.0-or-later", "dependencies": { - "@types/node-fetch": "^2.6.12", "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", - "ua-is-frozen": "^0.1.2" + "ua-is-frozen": "^0.1.2", + "undici": "^7.12.0" }, "bin": { "ua-parser-js": "script/cli.js" @@ -37,7 +37,6 @@ "@playwright/test": "^1.49.0", "jshint": "~2.13.6", "mocha": "~8.2.0", - "node-fetch": "^2.7.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", @@ -402,23 +401,6 @@ "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", "dev": true }, - "node_modules/@types/node": { - "version": "22.13.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", - "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", @@ -519,11 +501,6 @@ "node": ">=0.10.0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -664,17 +641,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -753,14 +719,6 @@ "node": ">=0.10.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/detect-europe-js": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.2.tgz", @@ -1011,19 +969,6 @@ "flat": "cli.js" } }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1591,25 +1536,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -2793,10 +2719,14 @@ "node": ">=0.8.0" } }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + "node_modules/undici": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", + "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } }, "node_modules/validate-npm-package-license": { "version": "3.0.4", diff --git a/package.json b/package.json index b39f0b008..e1477d8a9 100755 --- a/package.json +++ b/package.json @@ -227,7 +227,7 @@ "detect-europe-js": "^0.1.2", "is-standalone-pwa": "^0.1.1", "ua-is-frozen": "^0.1.2", - "@types/node-fetch": "^2.6.12" + "undici": "^7.12.0" }, "devDependencies": { "@babel/parser": "7.15.8", @@ -235,7 +235,6 @@ "@playwright/test": "^1.49.0", "jshint": "~2.13.6", "mocha": "~8.2.0", - "node-fetch": "^2.7.0", "requirejs": "2.3.2", "safe-regex": "^2.1.1", "tsd": "^0.29.0", diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 9533b6824..d1acf4bf5 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -2,8 +2,8 @@ // Project: https://github.com/faisalman/ua-parser-js // Definitions by: Faisal Salman -import type { IncomingHttpHeaders } from 'http'; -import type { Headers as FetchAPIHeaders } from 'node-fetch'; +import type { Headers } from "undici"; +import type { IncomingHttpHeaders } from "undici/types/header"; declare namespace UAParser { @@ -53,7 +53,7 @@ declare namespace UAParser { type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os'; type UAParserExt = Partial> | Partial>[]; - type UAParserHeaders = Record | IncomingHttpHeaders | FetchAPIHeaders; + type UAParserHeaders = Record | IncomingHttpHeaders | Headers; export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: UAParserHeaders): IResult; export function UAParser(uastring?: string, headers?: UAParserHeaders): IResult; diff --git a/test/unit/main.js b/test/unit/main.js index a2c797444..f9b2ef961 100644 --- a/test/unit/main.js +++ b/test/unit/main.js @@ -10,7 +10,7 @@ var cpus = require('../data/ua/cpu/cpu-all.json'); var devices = readJsonFiles('test/data/ua/device'); var engines = require('../data/ua/engine/engine-all.json'); var os = readJsonFiles('test/data/ua/os'); -var { Headers } = require('node-fetch'); +var { Headers } = require('undici'); function readJsonFiles(dir) { var list = []; From 74ef71cf63432c57e5805b4f4ec91a3fdc271b4e Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 10 Aug 2025 00:40:20 +0700 Subject: [PATCH 385/388] [extensions][helpers] Add new bots: Asana, bitlybot, Chrome-Lighthouse, DeepSeekBot, DuckDuckGo-Favicons-Bot, Elastic, Zoombot --- src/helpers/ua-parser-helpers.js | 3 +++ test/data/ua/extension/crawler.json | 30 ++++++++++++++++++++++ test/data/ua/extension/fetcher.json | 40 +++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index c00e49b68..c62a5417c 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -71,6 +71,9 @@ const isAIBot = (resultOrUA) => [ // DataForSeo 'dataforseobot', + // DeepSeek + 'deepseekbot', + // Diffbot 'diffbot', diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index ac580fc83..69743a084 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -359,6 +359,16 @@ "type" : "crawler" } }, + { + "desc" : "DeepSeekBot", + "ua" : "DeepSeekBot", + "expect" : + { + "name" : "DeepSeekBot", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Diffbot", "ua" : "Diffbot/0.1", @@ -389,6 +399,26 @@ "type" : "crawler" } }, + { + "desc" : "DuckDuckGo-Favicons-Bot", + "ua" : "DuckDuckGo-Favicons-Bot/1.0", + "expect" : + { + "name" : "DuckDuckGo-Favicons-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Elastic", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/124.0.6367.29 Safari/537.36 Elastic/Synthetics", + "expect" : + { + "name" : "Elastic", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Exabot", "ua" : "Mozilla/5.0 (compatible; Exabot/3.0; +http://www.exabot.com/go/robot)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 19b05bba0..e1f395184 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -9,6 +9,16 @@ "type" : "fetcher" } }, + { + "desc" : "Asana", + "ua" : "Asana/1.4.0 WebsiteMetadataRetriever", + "expect" : + { + "name" : "Asana", + "version" : "1.4.0", + "type" : "fetcher" + } + }, { "desc" : "Better Uptime Bot", "ua" : "Better Uptime Bot Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36", @@ -29,6 +39,16 @@ "type" : "fetcher" } }, + { + "desc" : "Bit.ly", + "ua" : "bitlybot/3.0 (+http://bit.ly/)", + "expect" : + { + "name" : "bitlybot", + "version" : "3.0", + "type" : "fetcher" + } + }, { "desc" : "Blueno", "ua" : "acebookexternalhit/1.1 (compatible; Blueno/1.0; +http://naver.me/scrap)", @@ -69,6 +89,16 @@ "type" : "fetcher" } }, + { + "desc" : "Chrome-Lighthouse", + "ua" : "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse", + "expect" : + { + "name" : "Chrome-Lighthouse", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "DuckAssistBot", "ua" : "DuckAssistBot/1.2; (+http://duckduckgo.com/duckassistbot.html)", @@ -288,5 +318,15 @@ "version" : "2.23.20.0", "type" : "fetcher" } + }, + { + "desc" : "Zoombot", + "ua" : "Mozilla/5.0 (compatible; Zoombot/1.0; +https://zoom.us; crawler@domain.com)", + "expect" : + { + "name" : "Zoombot", + "version" : "1.0", + "type" : "fetcher" + } } ] \ No newline at end of file From 95485f7b5d173b389021aaa9adc76d40c983194c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 11 Aug 2025 13:12:28 +0700 Subject: [PATCH 386/388] [extensions][helpers] Add new bots: cohere-training-data-crawler, Gemini-Deep-Research, kakaotalk-scrap, TikTokSpider --- src/extensions/ua-parser-extensions.js | 13 +++++------ src/helpers/ua-parser-helpers.js | 3 +++ test/data/ua/extension/crawler.json | 10 +++++++++ test/data/ua/extension/fetcher.json | 30 ++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 06ef90ff0..1f611a7bb 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -58,7 +58,7 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|coveo|criteo|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|cc|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -116,12 +116,13 @@ const Crawlers = Object.freeze({ // AI2Bot - https://allenai.org/crawler // Bytespider // DataForSeoBot - https://dataforseo.com/dataforseo-bot + // DeepSeekBot // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot // ImagesiftBot - https://imagesift.com/about // Qihoo 360Spider // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|yahoo! slurp)/i + /\b(360spider-?(?:image|video)?|bytespider|cohere-training-data-crawler|elastic(?=\/s)|(?:ai2|aspiegel|dataforseo|deepseek|imagesift|petal|turnitin)bot|teoma|yahoo! slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -236,17 +237,17 @@ const Emails = Object.freeze({ const Fetchers = Object.freeze({ browser : [ [ + // Asana / Bitlybot / Better Uptime / BingPreview / Blueno / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit // Buffer Link Preview Bot - https://scraper.buffer.com/about/bots/link-preview-bot // ChatGPT-User - https://platform.openai.com/docs/plugins/bot // DuckAssistBot - https://duckduckgo.com/duckassistbot/ - // Better Uptime / BingPreview / Blueno / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot // Google Site Verifier / Meta / Yahoo! Japan // Iframely - https://iframely.com/docs/about // Perplexity-User - https://docs.perplexity.ai/guides/bots // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|mastodon|(?:bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|iframely|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|mastodon|(?:bitly|bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero|zoom)bot|google-site-verification|iframely|kakaotalk-scrap|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, @@ -263,8 +264,8 @@ const Fetchers = Object.freeze({ [NAME, VERSION, [TYPE, FETCHER]], [ - // Google Bots / Cohere / Snapchat / Vercelbot / Yandex Bots - /((?:better uptime |telegram|vercel)bot|cohere-ai|feedfetcher-google|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|yandex(?:sitelinks|userproxy))/i + // Google Bots / Chrome-Lighthouse / Cohere / Gemini-Deep-Research / Snapchat / TikTokSpider / Vercelbot / Yandex Bots + /((?:better uptime |telegram|vercel)bot|chrome-lighthouse|cohere-ai|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|tiktokspider|yandex(?:sitelinks|userproxy))/i ], [NAME, [TYPE, FETCHER]], ], diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index c62a5417c..94b0482c9 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -62,6 +62,9 @@ const isAIBot = (resultOrUA) => [ // ByteDance 'bytespider', + // Cohere + 'cohere-training-data-crawler', + // Common Crawl 'ccbot', diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 69743a084..73de47646 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -299,6 +299,16 @@ "type" : "crawler" } }, + { + "desc" : "cohere-training-data-crawler", + "ua" : "cohere-training-data-crawler (+crawler@cohere.ai)", + "expect" : + { + "name" : "cohere-training-data-crawler", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Coveobot", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) (compatible; Coveobot/2.0;+http://www.coveo.com/bot.html)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index e1f395184..088b76c45 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -109,6 +109,16 @@ "type" : "fetcher" } }, + { + "desc" : "Gemini-Deep-Research", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Gemini-Deep-Research; +https://gemini.google/overview/deep-research/) Chrome/135.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Gemini-Deep-Research", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "Google FeedFetcher", "ua" : "FeedFetcher-Google; (+http://www.google.com/feedfetcher.html)", @@ -189,6 +199,16 @@ "type" : "fetcher" } }, + { + "desc" : "kakaotalk-scrap", + "ua" : "facebookexternalhit/1.1; kakaotalk-scrap/1.0; +https://devtalk.kakao.com/t/scrap/33984", + "expect" : + { + "name" : "kakaotalk-scrap", + "version" : "1.0", + "type" : "fetcher" + } + }, { "desc" : "Meta-ExternalFetcher", "ua" : "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", @@ -289,6 +309,16 @@ "type" : "fetcher" } }, + { + "desc" : "TikTokSpider", + "ua" : "Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; TikTokSpider; ttspider-feedback@tiktok.com)", + "expect" : + { + "name" : "TikTokSpider", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "UptimeRobot", "ua" : "Mozilla/5.0 (compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)", From 647b6232bd55ff3053429de195973f1c310a4f57 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 14 Aug 2025 19:34:14 +0700 Subject: [PATCH 387/388] [extensions][helpers] Add some bots from Vercel: v0bot, vercel-favicon-bot, vercel-screenshot-bot, vercelflags, verceltracing --- src/extensions/ua-parser-extensions.js | 5 ++-- src/helpers/ua-parser-helpers.js | 4 +++ test/data/ua/extension/crawler.json | 10 +++++++ test/data/ua/extension/fetcher.json | 40 ++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 1f611a7bb..d20dab050 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -121,8 +121,9 @@ const Crawlers = Object.freeze({ // ImagesiftBot - https://imagesift.com/about // Qihoo 360Spider // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html + // v0bot - https://vercel.com/docs/bot-management // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp - /\b(360spider-?(?:image|video)?|bytespider|cohere-training-data-crawler|elastic(?=\/s)|(?:ai2|aspiegel|dataforseo|deepseek|imagesift|petal|turnitin)bot|teoma|yahoo! slurp)/i + /\b(360spider-?(?:image|video)?|bytespider|cohere-training-data-crawler|elastic(?=\/s)|(?:ai2|aspiegel|dataforseo|deepseek|imagesift|petal|turnitin|v0)bot|teoma|yahoo! slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -265,7 +266,7 @@ const Fetchers = Object.freeze({ [ // Google Bots / Chrome-Lighthouse / Cohere / Gemini-Deep-Research / Snapchat / TikTokSpider / Vercelbot / Yandex Bots - /((?:better uptime |telegram|vercel)bot|chrome-lighthouse|cohere-ai|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|tiktokspider|yandex(?:sitelinks|userproxy))/i + /((?:better uptime |telegram|vercel)bot|chrome-lighthouse|cohere-ai|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|tiktokspider|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i ], [NAME, [TYPE, FETCHER]], ], diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 94b0482c9..b7b88d2f0 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -61,6 +61,7 @@ const isAIBot = (resultOrUA) => [ // ByteDance 'bytespider', + 'tiktokspider', // Cohere 'cohere-training-data-crawler', @@ -112,6 +113,9 @@ const isAIBot = (resultOrUA) => [ // Velen.io 'velenpublicwebcrawler', + // Vercel + 'v0bot', + // Webz.io 'omgili', 'omgilibot', diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 73de47646..8887a45fc 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -880,6 +880,16 @@ "type" : "crawler" } }, + { + "desc" : "v0bot", + "ua" : "v0bot", + "expect" : + { + "name" : "v0bot", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Yahoo! Japan", "ua" : "Y!J-BRW/1.0 (https://www.yahoo-help.jp/app/answers/detail/p/595/a_id/42716)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 088b76c45..13c9f62b3 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -329,6 +329,26 @@ "type" : "fetcher" } }, + { + "desc" : "vercel-favicon-bot", + "ua" : "vercel-favicon-bot", + "expect" : + { + "name" : "vercel-favicon-bot", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "vercel-screenshot-bot", + "ua" : "vercel-screenshot-bot", + "expect" : + { + "name" : "vercel-screenshot-bot", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "Vercelbot", "ua" : "Vercelbot (+https://vercel.com)", @@ -339,6 +359,26 @@ "type" : "fetcher" } }, + { + "desc" : "vercelflags", + "ua" : "vercelflags", + "expect" : + { + "name" : "vercelflags", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "verceltracing", + "ua" : "verceltracing", + "expect" : + { + "name" : "verceltracing", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "WhatsApp", "ua" : "WhatsApp/2.23.20.0", From 975c4860f45f0cccf80974c6351a16ba07180f31 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 14 Aug 2025 20:42:17 +0700 Subject: [PATCH 388/388] [extensions][helpers] Add some new AI bots: Bravebot, Cotoyogi, FirecrawlAgent, HuggingFace-Bot, Kangaroo Bot, PanguBot, Replicate-Bot, RunPod-Bot, Together-Bot, xAI-Bot --- src/extensions/ua-parser-extensions.js | 9 ++- src/helpers/ua-parser-helpers.js | 25 +++++++ test/data/ua/extension/crawler.json | 100 +++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 3 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index d20dab050..4e93b00fe 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -43,6 +43,7 @@ const Crawlers = Object.freeze({ // AhrefsBot - https://ahrefs.com/robot // Amazonbot - https://developer.amazon.com/amazonbot // Bingbot / AdIdxBot - https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0 + // Bravebot - https://search.brave.com/help/brave-search-crawler // CCBot - https://commoncrawl.org/faq // Coveobot - https://connect.coveo.com/s/article/19648 // CriteoBot - https://www.criteo.com/criteo-crawler/ @@ -51,6 +52,7 @@ const Crawlers = Object.freeze({ // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot // iAskBot - https://iask.ai + // Kangaroo Bot - https://kangaroollm.com.au/kangaroo-bot/ // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ // MojeekBot - https://www.mojeek.com/bot.html @@ -58,7 +60,7 @@ const Crawlers = Object.freeze({ // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot // SeznamBot - http://napoveda.seznam.cz/seznambot-intro - /((?:adidx|ahrefs|amazon|bing|cc|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|brave|cc|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -103,8 +105,9 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot - /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i + // aiHitBot / Diffbot / FirecrawlAgent / HuggingFace-Bot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / PanguBot / Replicate-Bot / RunPod-Bot / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / Together-Bot / VelenPublicWebCrawler / xAI-Bot / YisouSpider / YouBot + // Cotoyogi - https://ds.rois.ac.jp/en_center8/en_crawler/ + /((?:aihit|diff|huggingface-|pangu|replicate-|runpod-|timpi|together-|xai-|you)bot|omgili(?:bot)?|cotoyogi|firecrawlagent|openai image downloader|(?:magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index b7b88d2f0..912ccdb53 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -59,6 +59,9 @@ const isAIBot = (resultOrUA) => [ 'applebot', 'applebot-extended', + // Brave + 'bravebot', + // ByteDance 'bytespider', 'tiktokspider', @@ -92,6 +95,16 @@ const isAIBot = (resultOrUA) => [ // Huawei 'petalbot', + 'pangubot', + + // Hugging Face + 'huggingface-bot', + + // Kangaroo + 'kangaroo bot', + + // Mendable.ai + 'firecrawlagent', // Meta 'facebookbot', @@ -104,12 +117,21 @@ const isAIBot = (resultOrUA) => [ // Perplexity 'perplexitybot', + // Replicate + 'replicate-bot', + + // Runpod + 'runpod-bot', + // Semrush 'semrushbot-ocob', // Timpi 'timpibot', + // Together AI + 'together-bot', + // Velen.io 'velenpublicwebcrawler', @@ -121,6 +143,9 @@ const isAIBot = (resultOrUA) => [ 'omgilibot', 'webzio-extended', + // X + 'xai-bot', + // You.com 'youbot', diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 8887a45fc..f754b6650 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -229,6 +229,16 @@ "type" : "crawler" } }, + { + "desc" : "Bravebot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Bravebot/1.0; +https://search.brave.com/help/brave-search-crawler) Chrome/W.X.Y.Z Safari/537.36", + "expect" : + { + "name" : "Bravebot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Bytespider", "ua" : "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider", @@ -309,6 +319,16 @@ "type" : "crawler" } }, + { + "desc" : "Cotoyogi", + "ua" : "Mozilla/5.0 (compatible; Cotoyogi/4.0; +https://ds.rois.ac.jp/center8/crawler/)", + "expect" : + { + "name" : "Cotoyogi", + "version" : "4.0", + "type" : "crawler" + } + }, { "desc" : "Coveobot", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) (compatible; Coveobot/2.0;+http://www.coveo.com/bot.html)", @@ -469,6 +489,16 @@ "type" : "crawler" } }, + { + "desc" : "FirecrawlAgent", + "ua" : "Mozilla/5.0 (compatible; FirecrawlAgent/1.0)", + "expect" : + { + "name" : "FirecrawlAgent", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Googlebot-Video", "ua" : "Googlebot-Video/1.0", @@ -589,6 +619,16 @@ "type" : "crawler" } }, + { + "desc" : "HuggingFace-Bot", + "ua" : "Mozilla/5.0 (compatible; HuggingFace-Bot/1.0; +https://huggingface.co/)", + "expect" : + { + "name" : "HuggingFace-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "iAskBot", "ua" : "Mozilla/5.0 AppleWebKit/605.1.15 (KHTML, like Gecko; compatible; iAskBot/1.0; +https://iask.ai/) Chrome/120.0.6099.119 Safari/605.1.15", @@ -609,6 +649,16 @@ "type" : "crawler" } }, + { + "desc" : "Kangaroo Bot", + "ua" : "Mozilla/5.0 (compatible; Kangaroo Bot/1.0)", + "expect" : + { + "name" : "Kangaroo Bot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Linespider", "ua" : "Mozilla/5.0 (compatible; Linespider/1.1; +https://lin.ee/4dwXkTH)", @@ -710,6 +760,16 @@ "type" : "crawler" } }, + { + "desc" : "PanguBot", + "ua" : "Mozilla/5.0 (compatible; PanguBot/1.0)", + "expect" : + { + "name" : "PanguBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "PerplexityBot", "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)", @@ -770,6 +830,26 @@ "type" : "crawler" } }, + { + "desc" : "Replicate-Bot", + "ua" : "Mozilla/5.0 (compatible; Replicate-Bot/1.0; +https://replicate.com/)", + "expect" : + { + "name" : "Replicate-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "RunPod-Bot", + "ua" : "Mozilla/5.0 (compatible; RunPod-Bot/1.0; +https://runpod.io/)", + "expect" : + { + "name" : "RunPod-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "SemrushBot", "ua" : "Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)", @@ -860,6 +940,16 @@ "type" : "crawler" } }, + { + "desc" : "Together-Bot", + "ua" : "Mozilla/5.0 (compatible; Together-Bot/1.0; +https://together.ai/)", + "expect" : + { + "name" : "Together-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "TurnitinBot", "ua" : "TurnitinBot (https://turnitin.com/robot/crawlerinfo.html)", @@ -870,6 +960,16 @@ "type" : "crawler" } }, + { + "desc" : "xAI-Bot", + "ua" : "Mozilla/5.0 (compatible; xAI-Bot/1.0; +https://x.ai/)", + "expect" : + { + "name" : "xAI-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "VelenPublicWebCrawler", "ua" : "Mozilla/5.0 (compatible; VelenPublicWebCrawler/1.0; +https://velen.io)",