Compare commits

...

25 Commits

Author SHA1 Message Date
Faisal Salman
5328642e18 Update version to 2.0.0-beta.3 2024-06-08 01:17:52 +07:00
Faisal Salman
bdcd927304 Update test for extensions 2024-06-08 00:27:28 +07:00
Faisal Salman
db3423a76c 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
2024-06-08 00:06:27 +07:00
Faisal Salman
173325faa1 Add some well-known bot user-agents: Applebot, Amazonbot, Bytespider, Claudebot, Yandexbot 2024-06-06 22:36:15 +07:00
Faisal Salman
5190905df8 Clean up & few changes related to browser.type 2024-06-06 21:52:16 +07:00
Faisal Salman
0a46ac396a Fix #718 - Extension param now accept multiple extensions 2024-06-06 20:25:43 +07:00
Faisal Salman
f7810dbfcf Add new browsers: Wolvic & Pico Browser 2024-06-06 14:32:59 +07:00
Faisal Salman
0543b87c02 BREAKING CHANGE: AR/VR devices moved to new device type: xr 2024-06-05 15:49:07 +07:00
Faisal Salman
39590f112d BREAKING CHANGE - Add new property to browser: type 2024-06-02 23:04:25 +07:00
Faisal Salman
1a22c6951f Update all package references in /test to use current working directories 2024-06-02 22:39:33 +07:00
Faisal Salman
8991d34e56 Update formFactor -> formFactors, in accordance to the latest change in client hints spec 2024-06-02 21:56:36 +07:00
Faisal Salman
1a2ef00509 Improve browser detection for QQBrowser 2024-06-02 15:49:27 +07:00
Faisal Salman
12c2c2e48a Improve browser detection for Rekonq 2024-06-02 15:35:33 +07:00
Faisal Salman
85bf7076d3 Improve browser detection for ICEBrowser 2024-06-02 15:32:41 +07:00
Faisal Salman
1fa3d02594 Remove Viera from list of browsers 2024-06-02 15:20:50 +07:00
Faisal Salman
4cd867a36e Improve browser detection for Klar
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox#klar_for_android
2024-06-02 15:14:42 +07:00
Faisal Salman
6b6fcc68f5 Improve browser detection for Sleipnir 2024-06-02 15:10:37 +07:00
Faisal Salman
760e85bbe7 Update test for some missing browsers:
Blazer, Comodo Dragon, Conkeror, Go Browser, Iron, Jasmine, Links, NetSurf, OviBrowser, Quark, Rekonq, w3m
2024-06-02 15:03:27 +07:00
Faisal Salman
5a8ce35054 Insert spaces to command line output for readability 2024-06-02 10:38:24 +07:00
Faisal Salman
150d3c6b4a Add new feature: parse user-agent in CLI using npx ua-parser-js "[INSERT-UA-HERE]" and print the result in JSON format 2024-06-01 17:59:58 +07:00
Faisal Salman
e87c794fd9 Fix #730 - Improve browser detection: DuckDuckGo 2024-05-23 18:14:23 +07:00
Faisal Salman
d0db40c290 Fix #722 - Add new browser name: Twitter 2024-05-17 23:02:01 +07:00
Faisal Salman
8dce4cc514 Fix #721 - Improve detection: recognize OPPO Pad as tablet 2024-05-17 22:39:11 +07:00
Dai Jie
a43d659577 Fix #710: Add type to IBrowser (#711) 2024-03-19 21:32:43 +07:00
Faisal Salman
b29a9a7ffb Fix #708 - Improve detection for Quest 3 2024-02-28 10:19:30 +07:00
33 changed files with 1057 additions and 272 deletions

View File

@@ -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

View File

@@ -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
</tr>
<tr>
<td>Price</td>
<td><strong title="Pay as you want">FREE</strong></td>
<td><strong title="Pay as you want">FREE</strong></td>
<td><strong title="$12 (one-time fee)">$12</strong></td>
<td><strong title="$25 (one-time fee)">$25</strong></td>
<td><strong title="$500 (one-time fee)">$500</strong></td>
<td><strong title="Pay as you want">FREE (<a target="_blank" href="https://raw.githubusercontent.com/faisalman/ua-parser-js/1.0.x/license.md">License</a>)</strong></td>
<td><strong title="Pay as you want">FREE (<a target="_blank" href="https://raw.githubusercontent.com/faisalman/ua-parser-js/master/LICENSE.md">License</a>)</strong></td>
<td><strong title="$12 (one-time fee)">$12 (<a target="_blank" href="https://raw.githubusercontent.com/faisalman/ua-parser-js/pro-personal/LICENSE.md">License</a>)</strong></td>
<td><strong title="$25 (one-time fee)">$25 (<a target="_blank" href="https://raw.githubusercontent.com/faisalman/ua-parser-js/pro-business/LICENSE.md">License</a>)</strong></td>
<td><strong title="$500 (one-time fee)">$500 (<a target="_blank" href="https://raw.githubusercontent.com/faisalman/ua-parser-js/pro-enterprise/LICENSE.md">License</a>)</strong></td>
</tr>
</tbody>
<tfoot>
@@ -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.
<a href="https://github.com/faisalman/ua-parser-js/graphs/contributors">
<img src="https://contrib.rocks/image?repo=faisalman/ua-parser-js" />

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7
package-lock.json generated
View File

@@ -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",

View File

@@ -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'

View File

@@ -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 <f@faisalman.com> (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": [
@@ -196,6 +197,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",
@@ -205,7 +207,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": {

4
script/cli.js Executable file
View File

@@ -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)), null, 4));

View File

@@ -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 <f@faisalman.com>
AGPLv3 License */
@@ -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',
@@ -104,6 +104,7 @@ const Browser = Object.freeze({
PALEMOON: 'PaleMoon',
PHANTOMJS: 'PhantomJS',
PHOENIX: 'Phoenix',
PICOBROWSER: 'Pico Browser',
POLARIS: 'Polaris',
PUFFIN: 'Puffin',
QQ: 'QQBrowser',
@@ -128,9 +129,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',
@@ -139,11 +140,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',
@@ -171,7 +182,8 @@ const Device = Object.freeze({
MOBILE: 'mobile',
SMARTTV: 'smarttv',
TABLET: 'tablet',
WEARABLE: 'wearable'
WEARABLE: 'wearable',
XR: 'xr'
});
const Vendor = Object.freeze({
@@ -341,7 +353,8 @@ const OS = Object.freeze({
});
module.exports = {
Browser,
Browser,
BrowserType,
CPU,
Device,
Vendor,

View File

@@ -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 <f@faisalman.com>
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,

View File

@@ -1,13 +1,14 @@
// 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 <https://github.com/faisalman>
import type { UAParserExt } from "../main/ua-parser";
export const Apps: UAParserExt;
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;

View File

@@ -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 <f@faisalman.com>
AGPLv3 License */
@@ -14,33 +14,74 @@ 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({
browser : [
[/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']]
]
});
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']],
// Slackbot - https://api.slack.com/robots
[/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
]
});
//////////////////////
// COMMAND LINE APPS
/////////////////////
const CLIs = Object.freeze({
browser : [
// wget / curl / lynx
[/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
// wget / curl / lynx
[/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]]
]
});
////////////////////////
// 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
[/((?: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]]
]
});
//////////////////
// EXTRA DEVICES
/////////////////
const ExtraDevices = Object.freeze({
device : [[
/(nook)[\w ]+build\/(\w+)/i, // Nook
@@ -117,13 +158,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 : [[
@@ -230,19 +321,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]]
]
});
module.exports = {
Apps,
Bots,
CLIs,
Crawlers,
ExtraDevices,
Emails,
Fetchers,
InApps,
MediaPlayers,
Modules
};

View File

@@ -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 <f@faisalman.com>
AGPLv3 License */
@@ -18,33 +18,74 @@ 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({
browser : [
[/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']]
]
});
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']],
// Slackbot - https://api.slack.com/robots
[/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
]
});
//////////////////////
// COMMAND LINE APPS
/////////////////////
const CLIs = Object.freeze({
browser : [
// wget / curl / lynx
[/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
// wget / curl / lynx
[/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]]
]
});
////////////////////////
// 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
[/((?: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]]
]
});
//////////////////
// EXTRA DEVICES
/////////////////
const ExtraDevices = Object.freeze({
device : [[
/(nook)[\w ]+build\/(\w+)/i, // Nook
@@ -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
};

View File

@@ -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 <https://github.com/faisalman>

View File

@@ -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 <f@faisalman.com>
AGPLv3 License */

View File

@@ -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 <f@faisalman.com>
AGPLv3 License */

View File

@@ -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 <https://github.com/faisalman>
@@ -15,6 +15,7 @@ declare namespace UAParser {
name?: string;
version?: string;
major?: string;
type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module';
}
interface ICPU extends IData<ICPU> {
@@ -22,7 +23,7 @@ declare namespace UAParser {
}
interface IDevice extends IData<IDevice> {
type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable';
type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable' | 'xr' | 'embedded';
vendor?: string;
model?: string;
}
@@ -48,7 +49,7 @@ declare namespace UAParser {
type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[];
type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os';
type UAParserExt = Partial<Record<UAParserProps, RegexMap>>;
type UAParserExt = Partial<Record<UAParserProps, RegexMap>> | Partial<Record<UAParserProps, RegexMap>>[];
export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record<string, string>): IResult;
export function UAParser(uastring?: string, headers?: Record<string, string>): IResult;
@@ -61,6 +62,7 @@ declare namespace UAParser {
NAME: 'name';
VERSION: 'version';
MAJOR: 'major';
TYPE: 'type';
};
static readonly CPU: {
ARCHITECTURE: 'architecture';
@@ -74,6 +76,7 @@ declare namespace UAParser {
SMARTTV: 'smarttv';
TABLET: 'tablet';
WEARABLE: 'wearable';
XR: 'xr';
EMBEDDED: 'embedded';
};
static readonly ENGINE: {

View File

@@ -1,6 +1,6 @@
/////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.0-beta.2
Copyright © 2012-2023 Faisal Salman <f@faisalman.com>
/* UAParser.js v2.0.0-beta.3
Copyright © 2012-2024 Faisal Salman <f@faisalman.com>
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',
@@ -38,11 +38,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',
@@ -51,12 +52,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',
@@ -100,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] : []);
}
}
}
return mergedRegexes;
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 = {};
@@ -123,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) {
@@ -269,12 +279,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
};
@@ -309,17 +320,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
@@ -352,8 +366,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
@@ -366,7 +382,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], [
@@ -379,6 +395,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
@@ -418,23 +435,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]]
@@ -521,6 +539,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,
@@ -704,12 +724,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
@@ -840,7 +865,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]],
@@ -968,7 +993,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 {
@@ -1088,15 +1113,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);
}
@@ -1147,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)
}
@@ -1157,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;
}
@@ -1218,7 +1243,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]);

View File

@@ -3,8 +3,8 @@
// Source: /src/main/ua-parser.js
/////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.0-beta.2
Copyright © 2012-2023 Faisal Salman <f@faisalman.com>
/* UAParser.js v2.0.0-beta.3
Copyright © 2012-2024 Faisal Salman <f@faisalman.com>
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] : []);
}
}
}
return mergedRegexes;
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 = {};
@@ -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]);

View File

@@ -28,6 +28,7 @@ expectType<IBrowser>(browser);
expectType<string | undefined>(browser.name);
expectType<string | undefined>(browser.version);
expectType<string | undefined>(browser.major);
expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module' | undefined>(browser.type);
expectType<boolean>(browser.is(''));
expectType<string>(browser.toString());
expectType<IBrowser | PromiseLike<IBrowser>>(browser.withClientHints());

View File

@@ -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) {

View File

@@ -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', () => {
@@ -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},

View File

@@ -3,77 +3,91 @@ 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 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 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(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"});
// 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"});
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"});
});
});
// 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!');
}
});
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);
});
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 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 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(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"});
});
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/)';
// 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"});
});
});
describe('Testing regexes', () => {
let regexes;
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('Checking for potentially vulnerable regex', () => {
for (let regex of regexes) {
it('Test against `safe-regex` : ' + regex, () => {
assert.strictEqual(safe(regex), true);
});
}
});
});

View File

@@ -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', () => {

View File

@@ -4,12 +4,13 @@ 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');
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},
@@ -125,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 () {
@@ -463,22 +471,22 @@ 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) {
assert.strictEqual(ua.device.type, 'wearable');
assert.strictEqual(ua.device.type, 'xr');
});
UAParser(FFEInk).withClientHints().then(function (ua) {

View File

@@ -40,7 +40,7 @@ test('read client hints data', async ({ page }) => {
}
],
platform: 'New OS',
formFactor: 'New Form Factor'
formFactors: 'New Form Factor'
});
}
}

View File

@@ -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",
@@ -419,6 +449,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",
@@ -449,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",
@@ -708,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",
@@ -768,6 +828,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",
@@ -788,6 +868,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)",
@@ -808,6 +898,36 @@
"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" : "Pico Browser",
"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" : "Pico Browser",
"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",
"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",
@@ -1008,6 +1128,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",
@@ -1188,6 +1328,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",
@@ -1219,7 +1369,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" :
{
@@ -1228,6 +1378,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",
@@ -1238,6 +1408,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",
@@ -1357,6 +1537,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",
@@ -1469,7 +1680,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" :
{
@@ -1539,13 +1750,23 @@
}
},
{
"desc" : "Viera",
"ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)",
"desc" : "w3m",
"ua" : "w3m/0.5.1",
"expect" :
{
"name" : "VIERA",
"version" : "undefined",
"major" : "undefined"
"name" : "w3m",
"version" : "0.5.1",
"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"
}
},
{
@@ -1753,7 +1974,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"
}
@@ -1763,7 +1984,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"
}
@@ -1904,6 +2125,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",
@@ -2071,5 +2332,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"
}
}
]

View File

@@ -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"
}
}
]

View File

@@ -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"
}
}
]

View File

@@ -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"
}
}
]

View File

@@ -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"
}
}
]

View File

@@ -0,0 +1,12 @@
[
{
"desc" : "Scrapy",
"ua" : "Scrapy/1.5.0 (+https://scrapy.org)",
"expect" :
{
"name" : "Scrapy",
"version" : "1.5.0",
"type" : "module"
}
}
]

View File

@@ -1373,7 +1373,7 @@
"expect": {
"vendor": "Facebook",
"model": "Quest",
"type": "wearable"
"type": "xr"
}
},
{
@@ -1382,7 +1382,16 @@
"expect": {
"vendor": "Facebook",
"model": "Quest 2",
"type": "wearable"
"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"
}
},
{
@@ -1391,7 +1400,7 @@
"expect": {
"vendor": "Facebook",
"model": "Quest Pro",
"type": "wearable"
"type": "xr"
}
},
{
@@ -1502,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",