From 088383b9bd75003b9aeb1b67b5d2f4eabd8bac03 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 21 Jul 2025 11:43:26 +0700 Subject: [PATCH 01/30] 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 e8d3615..a8070fe 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 174d787..ecedb6d 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 b325e7b..7d8c28c 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 a4f68b1..e9b749e 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 02/30] 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 7d8c28c..0d5eb3d 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 2f28212..029a7c1 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 03/30] [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 4cd644f..06ef90f 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 8f79fe8..c00e49b 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 918ee98..ac580fc 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 4a05393..19b05bb 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 04/30] 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 7dd00ea..8fb1f5e 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 4ca4f0b..b39f0b0 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 05/30] 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 a8070fe..933bccb 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 ecedb6d..828e9e8 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 0d5eb3d..4051869 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 9f39de9..a2a6379 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 06/30] 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 7d65516..22782df 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 3cb0831..c8afed4 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 94f5cc5..fec42a7 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 03863a2..8ab1e0e 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 4051869..b7edcf1 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 07/30] 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 933bccb..aa40b50 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 828e9e8..9aa28e6 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 b7edcf1..5f2d042 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 55329b3..06a70fe 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 0000000..ebc5af0 --- /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 08/30] 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 8fb1f5e..1265e3d 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 b39f0b0..e1477d8 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 9533b68..d1acf4b 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 a2c7974..f9b2ef9 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 09/30] [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 c00e49b..c62a541 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 ac580fc..69743a0 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 19b05bb..e1f3951 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 10/30] [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 06ef90f..1f611a7 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 c62a541..94b0482 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 69743a0..73de476 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 e1f3951..088b76c 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 11/30] [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 1f611a7..d20dab0 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 94b0482..b7b88d2 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 73de476..8887a45 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 088b76c..13c9f62 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 12/30] [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 d20dab0..4e93b00 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 b7b88d2..912ccdb 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 8887a45..f754b66 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)", From 9003fe572452dc9832e989dfabec3d8bdf753983 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 15 Aug 2025 20:42:41 +0700 Subject: [PATCH 13/30] [extensions] Add new bots: Algolia Crawler, contxbot, HubSpot Page Fetcher, Kagibot --- src/extensions/ua-parser-extensions.js | 12 ++++++----- test/data/ua/extension/crawler.json | 30 ++++++++++++++++++++++++++ test/data/ua/extension/fetcher.json | 10 +++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 4e93b00..5a29b50 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -45,6 +45,7 @@ const Crawlers = Object.freeze({ // 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 + // contxbot - https://affiliate-program.amazon.com/help/node/topic/GT98G5PPRERNVZ2C // Coveobot - https://connect.coveo.com/s/article/19648 // CriteoBot - https://www.criteo.com/criteo-crawler/ // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot @@ -52,6 +53,7 @@ const Crawlers = Object.freeze({ // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ // GPTBot - https://platform.openai.com/docs/gptbot // iAskBot - https://iask.ai + // Kagibot - https://kagi.com/bot // Kangaroo Bot - https://kangaroollm.com.au/kangaroo-bot/ // LinkedInBot - http://www.linkedin.com // MJ12bot - https://mj12bot.com/ @@ -60,7 +62,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|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, + /((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -105,9 +107,9 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\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 + // aiHitBot / Algolia Crawler / 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 + /((?:aihit|diff|huggingface-|pangu|replicate-|runpod-|timpi|together-|xai-|you)bot|omgili(?:bot)?|cotoyogi|firecrawlagent|openai image downloader|(?:algolia |magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -241,7 +243,7 @@ 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 + // Asana / Bitlybot / Better Uptime / BingPreview / Blueno / HubSpot Page Fetcher / 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 @@ -251,7 +253,7 @@ const Fetchers = Object.freeze({ // Perplexity-User - https://docs.perplexity.ai/guides/bots // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(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, + /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|hubspot page fetcher|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, diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index f754b66..f54aac7 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -79,6 +79,16 @@ "type" : "crawler" } }, + { + "desc" : "Algolia Crawler", + "ua" : "Algolia Crawler/v2.183.0", + "expect" : + { + "name" : "Algolia Crawler", + "version" : "v2.183.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)", @@ -319,6 +329,16 @@ "type" : "crawler" } }, + { + "desc" : "contxbot", + "ua" : "Mozilla/5.0 (compatible;contxbot/1.0)", + "expect" : + { + "name" : "contxbot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Cotoyogi", "ua" : "Mozilla/5.0 (compatible; Cotoyogi/4.0; +https://ds.rois.ac.jp/center8/crawler/)", @@ -649,6 +669,16 @@ "type" : "crawler" } }, + { + "desc" : "Kagibot", + "ua" : "Mozilla/5.0 (compatible; Kagibot/1.0; +https://kagi.com/bot)", + "expect" : + { + "name" : "Kagibot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Kangaroo Bot", "ua" : "Mozilla/5.0 (compatible; Kangaroo Bot/1.0)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 13c9f62..c1c8989 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -189,6 +189,16 @@ "type" : "fetcher" } }, + { + "desc" : "HubSpot Page Fetcher", + "ua" : "HubSpot Page Fetcher/1.0 http://www.hubspot.com/ web-crawlers@hubspot.com", + "expect" : + { + "name" : "HubSpot Page Fetcher", + "version" : "1.0", + "type" : "fetcher" + } + }, { "desc" : "Iframely", "ua" : "Iframely/1.3.1 (+https://iframely.com/docs/about)", From c9badeb345b78c6d577d6cd08e24780bf8ee0308 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 21 Aug 2025 21:40:50 +0700 Subject: [PATCH 14/30] [extensions] Add new crawlers: Algolia, Baidu, BLEXBot, Botify, Freespoke, Marginalia, MSNBot, OnCrawl, SeekportBot, Siteimprove, TwinAgent, YepBot, ZumBot --- src/extensions/ua-parser-extensions.js | 21 ++-- test/data/ua/extension/crawler.json | 142 ++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 8 deletions(-) diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5a29b50..6346b9f 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -62,7 +62,11 @@ 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|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + // YepBot - https://yep.com/yepbot/ + /((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam|yep)bot)\/([\w\.-]+)/i, + + // Algolia Crawler + /(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -89,6 +93,9 @@ const Crawlers = Object.freeze({ // Internet Archive (archive.org) /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + // OnCrawl + /(oncrawl) mobile\/([\w\.]+)/i, + // Qwantbot - https://help.qwant.com/bot /(qwantbot)[-\w]*\/?([\w\.]*)/i, @@ -107,9 +114,10 @@ const Crawlers = Object.freeze({ // Yeti (Naver) /(yeti)\/([\w\.]+)/i, - // aiHitBot / Algolia Crawler / 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 + // aiHitBot / Algolia Crawler / BLEXBot / Diffbot / FirecrawlAgent / HuggingFace-Bot / Linespider / MSNBot / 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 / ZumBot // 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|(?:algolia |magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i + // Freespoke - https://docs.freespoke.com/search/bot/ + /((?:aihit|blex|diff|huggingface-|msn|pangu|replicate-|runpod-|timpi|together-|xai-|you|zum)bot|(?:magpie-|velenpublicweb)crawler|(?:chatglm-|line|screaming frog seo |yisou)spider|cotoyogi|firecrawlagent|freespoke|omgili(?:bot)?|openai image downloader|startpageprivateimageproxy|twinagent|webzio-extended)\/?([\w\.]*)/i ], [NAME, VERSION, [TYPE, CRAWLER]], @@ -119,16 +127,15 @@ const Crawlers = Object.freeze({ /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, // 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 + // Siteimprove - https://help.siteimprove.com/support/solutions/articles/80000448553 // 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|v0)bot|teoma|yahoo! slurp)/i + // Botify / Bytespider / DeepSeekBot / Qihoo 360Spider / SeekportBot + /\b((?:ai2|aspiegel|dataforseo|deepseek|imagesift|petal|seekport|turnitin|v0)bot|360spider-?(?:image|video)?|baidu-ads|botify|bytespider|cohere-training-data-crawler|elastic(?=\/s)|marginalia|siteimprove(?=bot|\.com)|teoma|yahoo! slurp)/i ], [NAME, [TYPE, CRAWLER]] ] diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index f54aac7..3506077 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -89,6 +89,16 @@ "type" : "crawler" } }, + { + "desc" : "Algolia Crawler Renderscript", + "ua" : "Algolia Crawler Renderscript", + "expect" : + { + "name" : "Algolia Crawler Renderscript", + "version" : "undefined", + "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)", @@ -149,6 +159,16 @@ "type" : "crawler" } }, + { + "desc" : "Baidu ADS", + "ua" : "Baidu-ADS", + "expect" : + { + "name" : "Baidu-ADS", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Baiduspider", "ua" : "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", @@ -239,6 +259,26 @@ "type" : "crawler" } }, + { + "desc" : "BLEXBot", + "ua" : "Mozilla/5.0 (compatible; BLEXBot/1.0; +http://webmeup-crawler.com/)", + "expect" : + { + "name" : "BLEXBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "botify", + "ua" : "Desktop: Mozilla/5.0 (compatible; botify; http://botify.com)", + "expect" : + { + "name" : "botify", + "version" : "undefined", + "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", @@ -519,6 +559,16 @@ "type" : "crawler" } }, + { + "desc" : "Freespoke", + "ua" : "Mozilla/5.0 (compatible; Freespoke/2.0; +https://docs.freespoke.com/search/bot)", + "expect" : + { + "name" : "Freespoke", + "version" : "2.0", + "type" : "crawler" + } + }, { "desc" : "Googlebot-Video", "ua" : "Googlebot-Video/1.0", @@ -719,6 +769,16 @@ "type" : "crawler" } }, + { + "desc" : "Marginalia Search", + "ua" : "search.marginalia.nu", + "expect" : + { + "name" : "marginalia", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Meta-ExternalAgent", "ua" : "meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", @@ -750,6 +810,16 @@ "type" : "crawler" } }, + { + "desc" : "msnbot", + "ua" : "msnbot/2.0b (+http://search.msn.com/msnbot.htm)", + "expect" : + { + "name" : "msnbot", + "version" : "2.0b", + "type" : "crawler" + } + }, { "desc" : "Omgili", "ua" : "omgili/0.5 +https://omgili.com", @@ -770,6 +840,16 @@ "type" : "crawler" } }, + { + "desc" : "OnCrawl", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12F70 Safari/600.1.4 (compatible; OnCrawl Mobile/1.0; +http://www.oncrawl.com/)", + "expect" : + { + "name" : "OnCrawl", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Onespot", "ua" : "Mozilla/5.0 (compatible; Onespot-ScraperBot/1.0; +https://www.onespot.com/identifying-traffic.html)", @@ -880,6 +960,16 @@ "type" : "crawler" } }, + { + "desc" : "SeekportBot", + "ua" : "Mozilla/5.0 (compatible; SeekportBot; +https://bot.seekport.com)", + "expect" : + { + "name" : "SeekportBot", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "SemrushBot", "ua" : "Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)", @@ -931,7 +1021,27 @@ } }, { - "desc" : "Sogou", + "desc" : "Siteimprove", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; SiteCheck-sitecrawl by Siteimprove.com; +https://siteimprove.com/bots) Chrome/[VERSION] Safari/537.36", + "expect" : + { + "name" : "Siteimprove", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Sogou Pic Spider", + "ua" : "Sogou Pic Spider/3.0( http://www.sogou.com/docs/help/webmasters.htm#07)", + "expect" : + { + "name" : "Sogou Pic Spider", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "Sogou web spider", "ua" : "Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)", "expect" : { @@ -990,6 +1100,16 @@ "type" : "crawler" } }, + { + "desc" : "TwinAgent", + "ua" : "TwinAgent/1.0", + "expect" : + { + "name" : "TwinAgent", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "xAI-Bot", "ua" : "Mozilla/5.0 (compatible; xAI-Bot/1.0; +https://x.ai/)", @@ -1050,6 +1170,16 @@ "type" : "crawler" } }, + { + "desc" : "YepBot", + "ua" : "Mozilla/5.0 (compatible; YepBot/1.0; +http://yep.com/yepbot/)", + "expect" : + { + "name" : "YepBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "Yeti", "ua" : "Mozilla/5.0 (compatible; Yeti/1.1; +http://naver.me/spd)", @@ -1089,5 +1219,15 @@ "version" : "undefined", "type" : "crawler" } + }, + { + "desc" : "ZumBot", + "ua" : "Mozilla/5.0 (compatible; ZumBot/1.0; http://help.zum.com/inquiry)", + "expect" : + { + "name" : "ZumBot", + "version" : "1.0", + "type" : "crawler" + } } ] From 98cf19c8c5cc93a75a758bc8b536d7c237d67b44 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 23 Aug 2025 19:06:07 +0700 Subject: [PATCH 15/30] Main d.ts export type UAParserHeaders --- src/main/ua-parser.d.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index d1acf4b..088d2b0 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -3,7 +3,6 @@ // Definitions by: Faisal Salman import type { Headers } from "undici"; -import type { IncomingHttpHeaders } from "undici/types/header"; declare namespace UAParser { @@ -53,7 +52,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 | Headers; + export type UAParserHeaders = Record | Headers; export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: UAParserHeaders): IResult; export function UAParser(uastring?: string, headers?: UAParserHeaders): IResult; From 4e421e72fec0f961af2a1130ca171fc492a34c48 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 23 Aug 2025 19:42:20 +0700 Subject: [PATCH 16/30] Main d.ts: replace hardcoded type def with enum values --- src/main/ua-parser.d.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 088d2b0..9d49448 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -3,6 +3,7 @@ // Definitions by: Faisal Salman import type { Headers } from "undici"; +import { BrowserType, CPU as CPUArch, Device as DeviceType, Engine as EngineName } from "../enums/ua-parser-enums"; declare namespace UAParser { @@ -17,21 +18,21 @@ declare namespace UAParser { name?: string; version?: string; major?: string; - type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'library'; + type?: typeof BrowserType[keyof typeof BrowserType]; } interface ICPU extends IData { - architecture?: 'ia32' | 'ia64' | 'amd64' | 'arm' | 'arm64' | 'armhf' | 'avr' | 'avr32' | 'irix' | 'irix64' | 'mips' | 'mips64' | '68k' | 'pa-risc' | 'ppc' | 'sparc' | 'sparc64'; + architecture?: typeof CPUArch[keyof typeof CPUArch]; } interface IDevice extends IData { - type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable' | 'xr' | 'embedded'; + type?: typeof DeviceType[keyof typeof DeviceType]; vendor?: string; model?: string; } interface IEngine extends IData { - name?: 'Amaya' | 'ArkWeb' | 'Blink' | 'EdgeHTML' | 'Flow' | 'Gecko' | 'Goanna' | 'iCab' | 'KHTML' | 'LibWeb' | 'Links' | 'Lynx' | 'NetFront' | 'NetSurf' | 'Presto' | 'Servo' | 'Tasman' | 'Trident' | 'w3m' | 'WebKit'; + name?: typeof EngineName[keyof typeof EngineName]; version?: string; } From 3e65196b57c8a0b3ce210533c88e761a799179e0 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 26 Aug 2025 00:18:53 +0700 Subject: [PATCH 17/30] Normalize all headers into lowercase --- src/main/ua-parser.js | 19 ++++++++++++++----- test/unit/main.js | 8 ++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 5f2d042..506ca75 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1390,11 +1390,20 @@ 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 (headers) { + if (typeof headers.append === FUNC_TYPE) { + // Convert Headers object into a plain object + var kv = {}; + headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; }); + headers = kv; + } else { + // Normalize headers field name into lowercase + var normalized = {}; + for (var header in headers) { + normalized[String(header).toLowerCase()] = headers[header]; + } + headers = normalized; + } } if (!(this instanceof UAParser)) { diff --git a/test/unit/main.js b/test/unit/main.js index f9b2ef9..fd1118b 100644 --- a/test/unit/main.js +++ b/test/unit/main.js @@ -381,4 +381,12 @@ describe('Read user-agent data from req.headers', function () { const { browser } = UAParser(reqHeaders); assert.strictEqual(browser.is('Midori'), true); }); + + it('Headers field name should be case insensitive', function () { + const hEaDeRs = { + 'uSeR-aGenT' : 'Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+' + }; + const { browser } = UAParser(hEaDeRs); + assert.strictEqual(browser.toString(), "Midori 0.2.2"); + }); }); From fb1ed5cf6ba336973502dedfebaad2042eebd189 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 26 Aug 2025 22:29:08 +0700 Subject: [PATCH 18/30] Only check for direct properties from the headers object --- src/main/ua-parser.js | 4 +++- test/unit/main.js | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js index 506ca75..2030ae8 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1400,7 +1400,9 @@ // Normalize headers field name into lowercase var normalized = {}; for (var header in headers) { - normalized[String(header).toLowerCase()] = headers[header]; + if (headers.hasOwnProperty(header)) { + normalized[String(header).toLowerCase()] = headers[header]; + } } headers = normalized; } diff --git a/test/unit/main.js b/test/unit/main.js index fd1118b..b99e832 100644 --- a/test/unit/main.js +++ b/test/unit/main.js @@ -389,4 +389,10 @@ describe('Read user-agent data from req.headers', function () { const { browser } = UAParser(hEaDeRs); assert.strictEqual(browser.toString(), "Midori 0.2.2"); }); + + it('Empty headers should not raise any error', function () { + const emptyHeaders = {}; + const { browser } = UAParser(emptyHeaders); + assert.strictEqual(browser.toString(), "undefined"); + }); }); From 48a1f34c286b1ebe6500d4c6e139cb2d6da83b1b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 27 Aug 2025 10:41:00 +0700 Subject: [PATCH 19/30] [enums] Rename & mark as deprecated: `Browser`->`BrowserName`, `CPU`->`CPUName`, `Device`->`DeviceType`, `Vendor`->`DeviceVendor`, `Engine`->`EngineName`, `OS`->`OSName` --- src/enums/ua-parser-enums.d.ts | 774 +++++++++++++++++---------------- src/enums/ua-parser-enums.js | 54 ++- 2 files changed, 444 insertions(+), 384 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index aa40b50..ba5ea99 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -2,386 +2,416 @@ // 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"; - DAUM: "Daum"; - DILLO: "Dillo"; - DOLPHIN: "Dolphin"; +export const BrowserName: 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", + DAUM: "Daum", + DILLO: "Dillo", + DOLPHIN: "Dolphin", DOOBLE: 'Dooble', - DORIS: "Doris"; - DRAGON: "Dragon"; - DUCKDUCKGO: "DuckDuckGo"; - ECOSIA: "Ecosia"; - EDGE: "Edge"; - EDGE_WEBVIEW: "Edge WebView"; - EDGE_WEBVIEW2: "Edge WebView2"; - 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"; - LG: "LG Browser"; - 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"; + DORIS: "Doris", + DRAGON: "Dragon", + DUCKDUCKGO: "DuckDuckGo", + ECOSIA: "Ecosia", + EDGE: "Edge", + EDGE_WEBVIEW: "Edge WebView", + EDGE_WEBVIEW2: "Edge WebView2", + 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", + LG: "LG Browser", + 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_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"; - PHOENIX: "Phoenix"; - PICOBROWSER: "Pico Browser"; - POLARIS: "Polaris"; - PUFFIN: "Puffin"; - QQ: "QQBrowser"; - QQ_LITE: "QQBrowserLite"; - QUARK: "Quark"; - QUPZILLA: "QupZilla"; - QUTEBROWSER: "qutebrowser"; - 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"; - SURF: "Surf"; - 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"; - ZALO: "Zalo"; + OPERA_MINI: "Opera Mini", + OPERA_MOBI: "Opera Mobi", + OPERA_TABLET: "Opera Tablet", + OPERA_TOUCH: "Opera Touch", + OTTER: "Otter", + OVI: "OviBrowser", + PALEMOON: "PaleMoon", + PHANTOMJS: "PhantomJS", + PHOENIX: "Phoenix", + PICOBROWSER: "Pico Browser", + POLARIS: "Polaris", + PUFFIN: "Puffin", + QQ: "QQBrowser", + QQ_LITE: "QQBrowserLite", + QUARK: "Quark", + QUPZILLA: "QupZilla", + QUTEBROWSER: "qutebrowser", + 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", + SURF: "Surf", + 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", + ZALO: "Zalo", }>; +/** + * @deprecated Use `BrowserName` instead + */ +export const Browser = BrowserName; + export const BrowserType: Readonly<{ - CRAWLER: "crawler"; - CLI: "cli"; - EMAIL: "email"; - FETCHER: "fetcher"; - INAPP: "inapp"; - MEDIAPLAYER: "mediaplayer"; - LIBRARY: "library"; + CRAWLER: "crawler", + CLI: "cli", + EMAIL: "email", + FETCHER: "fetcher", + INAPP: "inapp", + MEDIAPLAYER: "mediaplayer", + LIBRARY: "library", }>; -export const CPU: Readonly<{ - '68K': "68k"; - ALPHA: "alpha"; - 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 CPUName: Readonly<{ + '68K': "68k", + ALPHA: "alpha", + 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"; +/** + * @deprecated Use `CPUName` instead + */ +export const CPU = CPUName; + +export const DeviceType: 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"; - BLU: "BLU"; - 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"; - LAVA: "Lava"; - 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"; - PHILIPS: "Philips"; - PICO: "Pico"; - POLYTRON: "Polytron"; - REALME: "Realme"; - RETROID: "Retroid"; - 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"; - VIZIO: "Vizio"; - VODAFONE: "Vodafone"; - XBOX: "Xbox"; - XIAOMI: "Xiaomi"; - ZEBRA: "Zebra"; - ZTE: "ZTE"; +/** + * @deprecated Use `DeviceType` instead + */ +export const Device = DeviceType; + +export const DeviceVendor: Readonly<{ + ACER: "Acer", + ADVAN: "Advan", + ALCATEL: "Alcatel", + APPLE: "Apple", + AMAZON: "Amazon", + ARCHOS: "Archos", + ASUS: "ASUS", + ATT: "AT&T", + BENQ: "BenQ", + BLACKBERRY: "BlackBerry", + BLU: "BLU", + 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", + LAVA: "Lava", + 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", + PHILIPS: "Philips", + PICO: "Pico", + POLYTRON: "Polytron", + REALME: "Realme", + RETROID: "Retroid", + 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", + VIZIO: "Vizio", + 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"; +/** + * @deprecated Use `DeviceVendor` instead + */ +export const Vendor = DeviceVendor; + +export const EngineName: 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"; - ARCAOS: "ArcaOS"; - 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"; - KNOPPIX: "Knoppix"; - 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"; +/** + * @deprecated Use `EngineName` instead + */ +export const Engine = EngineName; + +export const OSName: Readonly<{ + AIX: "AIX", + AMIGA_OS: "Amiga OS", + ANDROID: "Android", + ANDROID_X86: "Android-x86", + ARCAOS: "ArcaOS", + 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", + KNOPPIX: "Knoppix", + 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", UBUNTU_TOUCH: "Ubuntu Touch", - UNIX: "Unix"; - VECTORLINUX: "VectorLinux"; - 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"; + UNIX: "Unix", + VECTORLINUX: "VectorLinux", + 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", }>; +/** + * @deprecated Use `OSName` instead + */ +export const OS = OSName; \ No newline at end of file diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 9aa28e6..9a77905 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -7,7 +7,7 @@ /*jshint esversion: 6 */ -const Browser = Object.freeze({ +const BrowserName = Object.freeze({ '115': '115', '2345': '2345', '360': '360', @@ -163,6 +163,10 @@ const Browser = Object.freeze({ // TODO : test! }); +/** + * @deprecated Use `BrowserName` instead + */ +const Browser = BrowserName; const BrowserType = Object.freeze({ CRAWLER: 'crawler', @@ -174,7 +178,7 @@ const BrowserType = Object.freeze({ LIBRARY: 'library' }); -const CPU = Object.freeze({ +const CPUName = Object.freeze({ '68K': '68k', ALPHA: 'alpha', ARM : 'arm', @@ -194,8 +198,12 @@ const CPU = Object.freeze({ X86: 'ia32', X86_64: 'amd64' }); +/** + * @deprecated Use `CPUName` instead + */ +const CPU = CPUName; -const Device = Object.freeze({ +const DeviceType = Object.freeze({ CONSOLE: 'console', DESKTOP: 'desktop', EMBEDDED: 'embedded', @@ -205,8 +213,12 @@ const Device = Object.freeze({ WEARABLE: 'wearable', XR: 'xr' }); +/** + * @deprecated Use `DeviceType` instead + */ +const Device = DeviceType; -const Vendor = Object.freeze({ +const DeviceVendor = Object.freeze({ ACER: 'Acer', ADVAN: 'Advan', ALCATEL: 'Alcatel', @@ -282,8 +294,12 @@ const Vendor = Object.freeze({ // TODO : test! }); +/** + * @deprecated Use `DeviceVendor` instead + */ +const Vendor = DeviceVendor; -const Engine = Object.freeze({ +const EngineName = Object.freeze({ AMAYA: 'Amaya', ARKWEB: 'ArkWeb', BLINK: 'Blink', @@ -305,8 +321,12 @@ const Engine = Object.freeze({ W3M: 'w3m', WEBKIT: 'WebKit' }); +/** + * @deprecated Use `EngineName` instead + */ +const Engine = EngineName; -const OS = Object.freeze({ +const OSName = Object.freeze({ AIX: 'AIX', AMIGA_OS: 'Amiga OS', ANDROID: 'Android', @@ -402,13 +422,23 @@ const OS = Object.freeze({ // TODO : test! }); +/** + * @deprecated Use `OSName` instead + */ +const OS = OSName; module.exports = { - Browser, + Browser,// deprecated + CPU, // deprecated + Device, // deprecated + Vendor, // deprecated + Engine, // deprecated + OS, // deprecated + BrowserName, BrowserType, - CPU, - Device, - Vendor, - Engine, - OS + CPUName, + DeviceType, + DeviceVendor, + EngineName, + OSName }; \ No newline at end of file From 0e05332609da8ba72a7cd933e8a5dd9e2806f555 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 27 Aug 2025 12:14:56 +0700 Subject: [PATCH 20/30] [enums] Add enums for `extensions` submodule --- src/enums/ua-parser-enums.d.ts | 254 +++++++++++++++++++++++++++++++- src/enums/ua-parser-enums.js | 255 ++++++++++++++++++++++++++++++++- 2 files changed, 507 insertions(+), 2 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index ba5ea99..bcff41c 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -414,4 +414,256 @@ export const OSName: Readonly<{ /** * @deprecated Use `OSName` instead */ -export const OS = OSName; \ No newline at end of file +export const OS = OSName; + +/*//////////////////////////////// + * Enums for Extensions submodule + */////////////////////////////// + +export const Extension: Readonly<{ + Browser: { + CLIs: { + CURL: 'curl', + ELINKS: 'ELinks', + HTTPIE: 'HTTPie', + LYNX: 'Lynx', + WGET: 'wget' + }, + Crawlers: { + '360_SPIDER': '360Spider', + AHREFS_BOT: 'AhrefsBot', + AI2_BOT: 'AI2Bot', + AIHIT_BOT: 'aiHitBot', + ALGOLIA_CRAWLER: 'Algolia Crawler', + APPLE_BOT: 'Applebot', + APPLE_BOT_EXTENDED: 'Applebot-Extended', + ASK_TEOMA: 'Teoma', + AMAZON_BOT: 'Amazonbot', + AMAZON_CONTXBOT: 'contxbot', + ANTHROPIC_AI: 'anthropic-ai', + ARCHIVEORG_BOT: 'archive.org_bot', + BAIDU_ADS: 'Baidu-ADS', + BAIDU_SPIDER: 'Baiduspider', + BAIDU_SPIDER_ADS: 'Baiduspider-ads', + BAIDU_SPIDER_CPRO: 'Baiduspider-cpro', + BAIDU_SPIDER_FAVO: 'Baiduspider-favo', + BAIDU_SPIDER_IMAGE: 'Baiduspider-image', + BAIDU_SPIDER_NEWS: 'Baiduspider-news', + BAIDU_SPIDER_RENDER: 'Baiduspider-render', + BAIDU_SPIDER_VIDEO: 'Baiduspider-video', + BLEX_BOT: 'BLEXBot', + BOTIFY: 'botify', + BRAVE_BOT: 'Bravebot', + BYTEDANCE_SPIDER: 'Bytespider', + CC_BOT: 'CCBot', + CHATGLM_SPIDER: 'ChatGLM-Spider', + CLAUDE_WEB: 'Claude-Web', + CLAUDE_BOT: 'ClaudeBot', + COCCOC_BOT_WEB: 'coccocbot-web', + COCCOC_BOT_IMAGE: 'coccocbot-image', + COHERE_TRAINING_DATA_CRAWLER: 'cohere-training-data-crawler', + COTOYOGI: 'Cotoyogi', + COVEO_BOT: 'Coveobot', + CRITEO_BOT: 'CriteoBot', + DATAFORSEO_BOT: 'DataForSeoBot', + DAUM: 'Daum', + DAUM_DAUMOA: 'Daumoa', + DAUM_DAUMOA_IMAGE: 'Daumoa-image', + DEEPSEEK_BOT: 'DeepSeekBot', + DIFFBOT: 'Diffbot', + DUCKDUCKGO_BOT: 'DuckDuckBot', + DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot', + ELASTIC: 'Elastic', + EXALEAD_EXABOT: 'Exabot', + FIRECRAWL_AGENT: 'FirecrawlAgent', + FREESPOKE: 'Freespoke', + GOOGLE_ADSBOT: 'AdsBot-Google', + GOOGLE_ADSBOT_MOBILE: 'Adsbot-Google-Mobile', + GOOGLE_ADSENSE: 'AdSense', + GOOGLE_BOT: 'Googlebot', + GOOGLE_BOT_IMAGE: 'Googlebot-Image', + GOOGLE_BOT_NEWS: 'Googlebot-News', + GOOGLE_BOT_VIDEO: 'Googlebot-Video', + GOOGLE_INSPECTIONTOOL: 'Google-InspectionTool', + GOOGLE_OTHER: 'GoogleOther', + GOOGLE_OTHER_IMAGE: 'GoogleOther-Image', + GOOGLE_OTHER_VIDEO: 'GoogleOther-Video', + GOOGLE_SAFETY: 'Google-Safety', + GOOGLE_STOREBOT: 'Storebot-Google', + HIVE_IMAGESIFTBOT: 'ImagesiftBot', + HUAWEI_PANGUBOT: 'PanguBot', + HUAWEI_PETALBOT: 'PetalBot', + HUGGINGFACE_BOT: 'HuggingFace-Bot', + HUNTER_VELENPUBLICWEBCRAWLER: 'VelenPublicWebCrawler', + IA_ARCHIVER: 'ia_archiver', + IASK_BOT: 'iAskBot', + KAGI_BOT: 'Kagibot', + KANGAROO_BOT: 'Kangaroo Bot', + LINE_SPIDER: 'Linespider', + LINKEDIN_BOT: 'LinkedInBot', + MAGPIE_CRAWLER: 'magpie-crawler', + MARGINALIA: 'marginalia', + META_EXTERNALAGENT: 'meta-externalagent', + META_FACEBOOKBOT: 'FacebookBot', + META_FACEBOOKCATALOG: 'facebookcatalog', + META_FACEBOOKEXTERNALHIT: 'facebookexternalhit', + MAJESTIC_MJ12BOT: 'MJ12bot', + MICROSOFT_BINGBOT: 'Bingbot', + MICROSOFT_MSNBOT: 'msnbot', + MICROSOFT_ADIDXBOT: 'adidxbot', + MOJEEK_BOT: 'MojeekBot', + MOZ_DOTBOT: 'DotBot', + OMGILI: 'omgili', + OMGILI_BOT: 'omgilibot', + ONCRAWL: 'OnCrawl', + ONESPOT_SCRAPERBOT: 'Onespot-ScraperBot', + OPENAI_GPTBOT: 'GPTBot', + OPENAI_SEARCH: 'OAI-SearchBot', + PERPLEXITY_BOT: 'PerplexityBot', + QWANT_BOT: 'Qwantbot', + REPLICATE_BOT: 'Replicate-Bot', + RUNPOD_BOT: 'RunPod-Bot', + SEEKPORT_BOT: 'SeekportBot', + SEMRUSH_BOT: 'SemrushBot', + SEMRUSH_BOT_BACKLINK: 'SemrushBot-BA', + SEMRUSH_BOT_CONTENTSHAKE: 'SemrushBot-OCOB', + SEMRUSH_BOT_SEO_CHECKER: 'SemrushBot-SI', + SEZNAM_BOT: 'SeznamBot', + SITEIMPROVE: 'Siteimprove', + SOGOU_PIC_SPIDER: 'Sogou Pic Spider', + SOGOU_WEB_SPIDER: 'Sogou web spider', + STARTPAGE: 'Startpage', + TIMPI_BOT: 'Timpibot', + TOGETHER_BOT: 'Together-Bot', + TURNITIN_BOT: 'TurnitinBot', + TWIN_AGENT: 'TwinAgent', + XAI_BOT: 'xAI-Bot', + VERCEL_V0BOT: 'v0bot', + YAHOO_JAPAN: 'Y!J-BRW', + YAHOO_SLURP: 'Yahoo! Slurp', + YANDEX_BOT: 'YandexBot', + YEP_BOT: 'YepBot', + YETI: 'Yeti', + YISOU_SPIDER: 'YisouSpider', + YOU_BOT: 'YouBot', + ZUM_BOT: 'ZumBot' + }, + Emails: { + AIRMAIL: 'Airmail', + APPLE_MAIL: 'Mail', + BLUEMAIL: 'BlueMail', + DAUM_MAIL: 'DaumMail', + EVOLUTION: 'Evolution', + EM_CLIENT: 'eM Client', + FOXMAIL: 'Foxmail', + KMAIL: 'KMail', + KMAIL2: 'kmail2', + KONTACT: 'Kontact', + MICROSOFT_OUTLOOK: 'Microsoft Outlook', + MICROSOFT_OUTLOOK_MAC: 'MacOutlook', + NAVER_MAILAPP: 'NaverMailApp', + POLYMAIL: 'Polymail', + PROTON_MAIL: 'ProtonMail', + SPARK_MAIL: 'SparkDesktop', + SPARROW: 'Sparrow', + THUNDERBIRD: 'Thunderbird', + YAHOO_MAIL: 'Yahoo', + ZIMBRA: 'Zimbra', + ZOHO_MAIL: 'ZohoMail-Desktop' + }, + Fetchers: { + AHREFS_SITEAUDIT: 'AhrefsSiteAudit', + ASANA: 'Asana', + BETTER_UPTIME_BOT: 'Better Uptime Bot', + BITLY_BOT: 'bitlybot', + BLUESKY: 'Bluesky', + BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot', + DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot', + GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', + GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', + GOOGLE_GEMINI_DEEP_RESEARCH: 'Gemini-Deep-Research', + GOOGLE_IMAGE_PROXY: 'GoogleImageProxy', + GOOGLE_PAGERENDERER: 'Google-PageRenderer', + GOOGLE_READ_ALOUD: 'Google-Read-Aloud', + GOOGLE_PRODUCER: 'GoogleProducer', + GOOGLE_SITE_VERIFICATION: 'Google-Site-Verification', + HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher', + IFRAMELY: 'Iframely', + KAKAOTALK_SCRAP: 'kakaotalk-scrap', + META_EXTERNALFETCHER: 'meta-externalfetcher', + MICROSOFT_BINGPREVIEW: 'BingPreview', + MICROSOFT_PREVIEW: 'MicrosoftPreview', + MISTRALAI_USER: 'MistralAI-User', + NAVER_BLUENO: 'Blueno', + ONCRAWL_ROGERBOT: 'rogerbot', + OPENAI_CHATGPT_USER: 'ChatGPT-User', + PERPLEXITY_USER: 'Perplexity-User', + PINTEREST_BOT: 'Pinterestbot', + SEMRUSH_SITEAUDITBOT: 'SiteAuditBot', + SNAP_URL_PREVIEW: 'Snap URL Preview', + SKYPE_URIPREVIEW: 'SkypeUriPreview', + TELEGRAM_BOT: 'TelegramBot', + TIKTOK_SPIDER: 'TikTokSpider', + UPTIMEROBOT: 'UptimeRobot', + VERCEL_FAVICON_BOT: 'vercel-favicon-bot', + VERCEL_SCREENSHOT_BOT: 'vercel-screenshot-bot', + VERCEL_BOT: 'Vercelbot', + VERCEL_FLAGS: 'vercelflags', + VERCEL_TRACING: 'verceltracing', + WHATSAPP: 'WhatsApp', + ZOOMINFO_BOT: 'Zoombot' + }, + InApps: { + DISCORD: 'Discord', + EVERNOTE: 'Evernote', + FIGMA: 'Figma', + FLIPBOARD: 'Flipboard', + MATTERMOST: 'Mattermost', + TEAMS: 'Teams', + NOTION: 'Notion', + POSTMAN: 'Postman', + RAMBOX: 'Rambox', + ROCKETCHAT: 'Rocket.Chat', + SLACK: 'Slack', + TIKTOK_LITE: 'TikTok Lite', + VSCODE: 'VS Code', + YAHOO_JAPAN: 'Yahoo! Japan' + }, + Libraries: { + ADOBE_AIR: 'AdobeAIR', + AIOHTTP: 'aiohttp', + APACHE_HTTPCLIENT: 'Apache-HttpClient', + AXIOS: 'axios', + GO_HTTP_CLIENT: 'go-http-client', + GOT: 'got', + GUZZLEHTTP: 'GuzzleHttp', + JAVA: 'Java', + JAVA_HTTPCLIENT: 'Java-http-client', + JSDOM: 'jsdom', + LIBWWW_PERL: 'libwww-perl', + LUA_RESTY_HTTP: 'lua-resty-http', + NEEDLE: 'Needle', + NUTCH: 'Nutch', + OKHTTP: 'OkHttp', + NODE_FETCH: 'node-fetch', + NODE_SUPERAGENT: 'node-superagent', + PHP_SOAP: 'PHP-SOAP', + POSTMAN_RUNTIME: 'PostmanRuntime', + PYTHON_HTTPX: 'python-httpx', + PYTHON_URLLIB: 'python-urllib', + PYTHON_URLLIB3: 'python-urllib3', + PYTHON_REQUESTS: 'python-requests', + SCRAPY: 'Scrapy' + } + }, + DeviceVendor: { + Vehicles: { + BMW: 'BMW', + BYD: 'BYD', + JEEP: 'Jeep', + RIVIAN: 'Rivian', + TESLA: 'Tesla', + VOLVO: 'Volvo' + } + } +}>; \ No newline at end of file diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 9a77905..7a1fcdc 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -427,6 +427,258 @@ const OSName = Object.freeze({ */ const OS = OSName; +/*//////////////////////////////// + * Enums for Extensions submodule + */////////////////////////////// + +const Extension = Object.freeze({ + BrowserName: { + CLIs: { + CURL: 'curl', + ELINKS: 'ELinks', + HTTPIE: 'HTTPie', + LYNX: 'Lynx', + WGET: 'wget' + }, + Crawlers: { + '360_SPIDER': '360Spider', + AHREFS_BOT: 'AhrefsBot', + AI2_BOT: 'AI2Bot', + AIHIT_BOT: 'aiHitBot', + ALGOLIA_CRAWLER: 'Algolia Crawler', + APPLE_BOT: 'Applebot', + APPLE_BOT_EXTENDED: 'Applebot-Extended', + ASK_TEOMA: 'Teoma', + AMAZON_BOT: 'Amazonbot', + AMAZON_CONTXBOT: 'contxbot', + ANTHROPIC_AI: 'anthropic-ai', + ARCHIVEORG_BOT: 'archive.org_bot', + BAIDU_ADS: 'Baidu-ADS', + BAIDU_SPIDER: 'Baiduspider', + BAIDU_SPIDER_ADS: 'Baiduspider-ads', + BAIDU_SPIDER_CPRO: 'Baiduspider-cpro', + BAIDU_SPIDER_FAVO: 'Baiduspider-favo', + BAIDU_SPIDER_IMAGE: 'Baiduspider-image', + BAIDU_SPIDER_NEWS: 'Baiduspider-news', + BAIDU_SPIDER_RENDER: 'Baiduspider-render', + BAIDU_SPIDER_VIDEO: 'Baiduspider-video', + BLEX_BOT: 'BLEXBot', + BOTIFY: 'botify', + BRAVE_BOT: 'Bravebot', + BYTEDANCE_SPIDER: 'Bytespider', + CC_BOT: 'CCBot', + CHATGLM_SPIDER: 'ChatGLM-Spider', + CLAUDE_WEB: 'Claude-Web', + CLAUDE_BOT: 'ClaudeBot', + COCCOC_BOT_WEB: 'coccocbot-web', + COCCOC_BOT_IMAGE: 'coccocbot-image', + COHERE_TRAINING_DATA_CRAWLER: 'cohere-training-data-crawler', + COTOYOGI: 'Cotoyogi', + COVEO_BOT: 'Coveobot', + CRITEO_BOT: 'CriteoBot', + DATAFORSEO_BOT: 'DataForSeoBot', + DAUM: 'Daum', + DAUM_DAUMOA: 'Daumoa', + DAUM_DAUMOA_IMAGE: 'Daumoa-image', + DEEPSEEK_BOT: 'DeepSeekBot', + DIFFBOT: 'Diffbot', + DUCKDUCKGO_BOT: 'DuckDuckBot', + DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot', + ELASTIC: 'Elastic', + EXALEAD_EXABOT: 'Exabot', + FIRECRAWL_AGENT: 'FirecrawlAgent', + FREESPOKE: 'Freespoke', + GOOGLE_ADSBOT: 'AdsBot-Google', + GOOGLE_ADSBOT_MOBILE: 'Adsbot-Google-Mobile', + GOOGLE_ADSENSE: 'AdSense', + GOOGLE_BOT: 'Googlebot', + GOOGLE_BOT_IMAGE: 'Googlebot-Image', + GOOGLE_BOT_NEWS: 'Googlebot-News', + GOOGLE_BOT_VIDEO: 'Googlebot-Video', + GOOGLE_INSPECTIONTOOL: 'Google-InspectionTool', + GOOGLE_OTHER: 'GoogleOther', + GOOGLE_OTHER_IMAGE: 'GoogleOther-Image', + GOOGLE_OTHER_VIDEO: 'GoogleOther-Video', + GOOGLE_SAFETY: 'Google-Safety', + GOOGLE_STOREBOT: 'Storebot-Google', + HIVE_IMAGESIFTBOT: 'ImagesiftBot', + HUAWEI_PANGUBOT: 'PanguBot', + HUAWEI_PETALBOT: 'PetalBot', + HUGGINGFACE_BOT: 'HuggingFace-Bot', + HUNTER_VELENPUBLICWEBCRAWLER: 'VelenPublicWebCrawler', + IA_ARCHIVER: 'ia_archiver', + IASK_BOT: 'iAskBot', + KAGI_BOT: 'Kagibot', + KANGAROO_BOT: 'Kangaroo Bot', + LINE_SPIDER: 'Linespider', + LINKEDIN_BOT: 'LinkedInBot', + MAGPIE_CRAWLER: 'magpie-crawler', + MARGINALIA: 'marginalia', + META_EXTERNALAGENT: 'meta-externalagent', + META_FACEBOOKBOT: 'FacebookBot', + META_FACEBOOKCATALOG: 'facebookcatalog', + META_FACEBOOKEXTERNALHIT: 'facebookexternalhit', + MAJESTIC_MJ12BOT: 'MJ12bot', + MICROSOFT_BINGBOT: 'Bingbot', + MICROSOFT_MSNBOT: 'msnbot', + MICROSOFT_ADIDXBOT: 'adidxbot', + MOJEEK_BOT: 'MojeekBot', + MOZ_DOTBOT: 'DotBot', + OMGILI: 'omgili', + OMGILI_BOT: 'omgilibot', + ONCRAWL: 'OnCrawl', + ONESPOT_SCRAPERBOT: 'Onespot-ScraperBot', + OPENAI_GPTBOT: 'GPTBot', + OPENAI_SEARCH: 'OAI-SearchBot', + PERPLEXITY_BOT: 'PerplexityBot', + QWANT_BOT: 'Qwantbot', + REPLICATE_BOT: 'Replicate-Bot', + RUNPOD_BOT: 'RunPod-Bot', + SEEKPORT_BOT: 'SeekportBot', + SEMRUSH_BOT: 'SemrushBot', + SEMRUSH_BOT_BACKLINK: 'SemrushBot-BA', + SEMRUSH_BOT_CONTENTSHAKE: 'SemrushBot-OCOB', + SEMRUSH_BOT_SEO_CHECKER: 'SemrushBot-SI', + SEZNAM_BOT: 'SeznamBot', + SITEIMPROVE: 'Siteimprove', + SOGOU_PIC_SPIDER: 'Sogou Pic Spider', + SOGOU_WEB_SPIDER: 'Sogou web spider', + STARTPAGE: 'Startpage', + TIMPI_BOT: 'Timpibot', + TOGETHER_BOT: 'Together-Bot', + TURNITIN_BOT: 'TurnitinBot', + TWIN_AGENT: 'TwinAgent', + XAI_BOT: 'xAI-Bot', + VERCEL_V0BOT: 'v0bot', + YAHOO_JAPAN: 'Y!J-BRW', + YAHOO_SLURP: 'Yahoo! Slurp', + YANDEX_BOT: 'YandexBot', + YEP_BOT: 'YepBot', + YETI: 'Yeti', + YISOU_SPIDER: 'YisouSpider', + YOU_BOT: 'YouBot', + ZUM_BOT: 'ZumBot' + }, + Emails: { + AIRMAIL: 'Airmail', + APPLE_MAIL: 'Mail', + BLUEMAIL: 'BlueMail', + DAUM_MAIL: 'DaumMail', + EVOLUTION: 'Evolution', + EM_CLIENT: 'eM Client', + FOXMAIL: 'Foxmail', + KMAIL: 'KMail', + KMAIL2: 'kmail2', + KONTACT: 'Kontact', + MICROSOFT_OUTLOOK: 'Microsoft Outlook', + MICROSOFT_OUTLOOK_MAC: 'MacOutlook', + NAVER_MAILAPP: 'NaverMailApp', + POLYMAIL: 'Polymail', + PROTON_MAIL: 'ProtonMail', + SPARK_MAIL: 'SparkDesktop', + SPARROW: 'Sparrow', + THUNDERBIRD: 'Thunderbird', + YAHOO_MAIL: 'Yahoo', + ZIMBRA: 'Zimbra', + ZOHO_MAIL: 'ZohoMail-Desktop' + }, + Fetchers: { + AHREFS_SITEAUDIT: 'AhrefsSiteAudit', + ASANA: 'Asana', + BETTER_UPTIME_BOT: 'Better Uptime Bot', + BITLY_BOT: 'bitlybot', + BLUESKY: 'Bluesky', + BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot', + DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot', + GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', + GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', + GOOGLE_GEMINI_DEEP_RESEARCH: 'Gemini-Deep-Research', + GOOGLE_IMAGE_PROXY: 'GoogleImageProxy', + GOOGLE_PAGERENDERER: 'Google-PageRenderer', + GOOGLE_READ_ALOUD: 'Google-Read-Aloud', + GOOGLE_PRODUCER: 'GoogleProducer', + GOOGLE_SITE_VERIFICATION: 'Google-Site-Verification', + HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher', + IFRAMELY: 'Iframely', + KAKAOTALK_SCRAP: 'kakaotalk-scrap', + META_EXTERNALFETCHER: 'meta-externalfetcher', + MICROSOFT_BINGPREVIEW: 'BingPreview', + MICROSOFT_PREVIEW: 'MicrosoftPreview', + MISTRALAI_USER: 'MistralAI-User', + NAVER_BLUENO: 'Blueno', + ONCRAWL_ROGERBOT: 'rogerbot', + OPENAI_CHATGPT_USER: 'ChatGPT-User', + PERPLEXITY_USER: 'Perplexity-User', + PINTEREST_BOT: 'Pinterestbot', + SEMRUSH_SITEAUDITBOT: 'SiteAuditBot', + SNAP_URL_PREVIEW: 'Snap URL Preview', + SKYPE_URIPREVIEW: 'SkypeUriPreview', + TELEGRAM_BOT: 'TelegramBot', + TIKTOK_SPIDER: 'TikTokSpider', + UPTIMEROBOT: 'UptimeRobot', + VERCEL_FAVICON_BOT: 'vercel-favicon-bot', + VERCEL_SCREENSHOT_BOT: 'vercel-screenshot-bot', + VERCEL_BOT: 'Vercelbot', + VERCEL_FLAGS: 'vercelflags', + VERCEL_TRACING: 'verceltracing', + WHATSAPP: 'WhatsApp', + ZOOMINFO_BOT: 'Zoombot' + }, + InApps: { + DISCORD: 'Discord', + EVERNOTE: 'Evernote', + FIGMA: 'Figma', + FLIPBOARD: 'Flipboard', + MATTERMOST: 'Mattermost', + TEAMS: 'Teams', + NOTION: 'Notion', + POSTMAN: 'Postman', + RAMBOX: 'Rambox', + ROCKETCHAT: 'Rocket.Chat', + SLACK: 'Slack', + TIKTOK_LITE: 'TikTok Lite', + VSCODE: 'VS Code', + YAHOO_JAPAN: 'Yahoo! Japan' + }, + Libraries: { + ADOBE_AIR: 'AdobeAIR', + AIOHTTP: 'aiohttp', + APACHE_HTTPCLIENT: 'Apache-HttpClient', + AXIOS: 'axios', + GO_HTTP_CLIENT: 'go-http-client', + GOT: 'got', + GUZZLEHTTP: 'GuzzleHttp', + JAVA: 'Java', + JAVA_HTTPCLIENT: 'Java-http-client', + JSDOM: 'jsdom', + LIBWWW_PERL: 'libwww-perl', + LUA_RESTY_HTTP: 'lua-resty-http', + NEEDLE: 'Needle', + NUTCH: 'Nutch', + OKHTTP: 'OkHttp', + NODE_FETCH: 'node-fetch', + NODE_SUPERAGENT: 'node-superagent', + PHP_SOAP: 'PHP-SOAP', + POSTMAN_RUNTIME: 'PostmanRuntime', + PYTHON_HTTPX: 'python-httpx', + PYTHON_URLLIB: 'python-urllib', + PYTHON_URLLIB3: 'python-urllib3', + PYTHON_REQUESTS: 'python-requests', + SCRAPY: 'Scrapy' + } + }, + DeviceVendor: { + Vehicles: { + BMW: 'BMW', + BYD: 'BYD', + JEEP: 'Jeep', + RIVIAN: 'Rivian', + TESLA: 'Tesla', + VOLVO: 'Volvo' + } + } +}); + module.exports = { Browser,// deprecated CPU, // deprecated @@ -440,5 +692,6 @@ module.exports = { DeviceType, DeviceVendor, EngineName, - OSName + OSName, + Extension }; \ No newline at end of file From f810a6d1d9ea349cf6ac7b14dffaa39219eafc7a Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Wed, 27 Aug 2025 14:56:50 +0700 Subject: [PATCH 21/30] Fix type mistake --- src/enums/ua-parser-enums.d.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index bcff41c..6fa6c59 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -159,7 +159,7 @@ export const BrowserName: Readonly<{ /** * @deprecated Use `BrowserName` instead */ -export const Browser = BrowserName; +export const Browser: typeof BrowserName; export const BrowserType: Readonly<{ CRAWLER: "crawler", @@ -194,7 +194,7 @@ export const CPUName: Readonly<{ /** * @deprecated Use `CPUName` instead */ -export const CPU = CPUName; +export const CPU: typeof CPUName; export const DeviceType: Readonly<{ CONSOLE: "console", @@ -209,7 +209,7 @@ export const DeviceType: Readonly<{ /** * @deprecated Use `DeviceType` instead */ -export const Device = DeviceType; +export const Device: typeof DeviceType; export const DeviceVendor: Readonly<{ ACER: "Acer", @@ -288,7 +288,7 @@ export const DeviceVendor: Readonly<{ /** * @deprecated Use `DeviceVendor` instead */ -export const Vendor = DeviceVendor; +export const Vendor: typeof DeviceVendor; export const EngineName: Readonly<{ AMAYA: "Amaya", @@ -315,7 +315,7 @@ export const EngineName: Readonly<{ /** * @deprecated Use `EngineName` instead */ -export const Engine = EngineName; +export const Engine: typeof EngineName; export const OSName: Readonly<{ AIX: "AIX", @@ -414,7 +414,7 @@ export const OSName: Readonly<{ /** * @deprecated Use `OSName` instead */ -export const OS = OSName; +export const OS: typeof OSName; /*//////////////////////////////// * Enums for Extensions submodule From 7dcbb8def3c17769bb8433be87bfc19ef1fb81ad Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 28 Aug 2025 11:11:32 +0700 Subject: [PATCH 22/30] [enums] Enum for CPU architecture should be `CPUArch` rather than `CPUName` --- src/enums/ua-parser-enums.d.ts | 6 +++--- src/enums/ua-parser-enums.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 6fa6c59..5d25f94 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -171,7 +171,7 @@ export const BrowserType: Readonly<{ LIBRARY: "library", }>; -export const CPUName: Readonly<{ +export const CPUArch: Readonly<{ '68K': "68k", ALPHA: "alpha", ARM: "arm", @@ -192,9 +192,9 @@ export const CPUName: Readonly<{ X86_64: "amd64", }>; /** - * @deprecated Use `CPUName` instead + * @deprecated Use `CPUArch` instead */ -export const CPU: typeof CPUName; +export const CPU: typeof CPUArch; export const DeviceType: Readonly<{ CONSOLE: "console", diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 7a1fcdc..94b1f12 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -178,7 +178,7 @@ const BrowserType = Object.freeze({ LIBRARY: 'library' }); -const CPUName = Object.freeze({ +const CPUArch = Object.freeze({ '68K': '68k', ALPHA: 'alpha', ARM : 'arm', @@ -199,9 +199,9 @@ const CPUName = Object.freeze({ X86_64: 'amd64' }); /** - * @deprecated Use `CPUName` instead + * @deprecated Use `CPUArch` instead */ -const CPU = CPUName; +const CPU = CPUArch; const DeviceType = Object.freeze({ CONSOLE: 'console', @@ -688,7 +688,7 @@ module.exports = { OS, // deprecated BrowserName, BrowserType, - CPUName, + CPUArch, DeviceType, DeviceVendor, EngineName, From 2078b1ec927a7c49e9cd07f3f6f8963566be7eb2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 29 Aug 2025 17:53:09 +0700 Subject: [PATCH 23/30] [enums] Clean up enum imports & create build script --- script/build-esm.js | 53 +- src/enums/ua-parser-enums.d.ts | 769 +++++++++++++------------ src/enums/ua-parser-enums.js | 7 +- src/extensions/ua-parser-extensions.js | 8 +- src/helpers/ua-parser-helpers.d.ts | 30 +- src/helpers/ua-parser-helpers.js | 8 +- src/main/ua-parser.d.ts | 2 +- test/data/ua/extension/crawler.json | 10 + test/data/ua/extension/fetcher.json | 20 + test/unit/es6.mjs | 8 +- 10 files changed, 485 insertions(+), 430 deletions(-) diff --git a/script/build-esm.js b/script/build-esm.js index 98f7ffb..78321dd 100755 --- a/script/build-esm.js +++ b/script/build-esm.js @@ -2,15 +2,18 @@ /* jshint esversion: 6 */ const fs = require('fs'); -const generateMJS = (module) => { - let { src, dest, title, replacements } = module; - let text = fs.readFileSync(src, 'utf-8'); - - replacements.push( +const defaultReplacements = { + mjs: [ [/const (.+?)\s*=\s*require\(\'\.(.+)\'\)/ig, 'import $1 from \'\.$2.mjs\''], [/const (.+?)\s*=\s*require\(\'(.+)\'\)/ig, 'import $1 from \'$2\''], [/module\.exports =/ig, 'export'] - ); + ] +} + +const generateFile = (module) => { + let { src, dest, title, replacements } = module; + let text = fs.readFileSync(src, 'utf-8'); + replacements.forEach(rep => { text = text.replace(rep[0], rep[1]); }); @@ -18,42 +21,54 @@ const generateMJS = (module) => { console.log(`Generate ${dest}`); fs.writeFileSync(dest, -`// Generated ESM version of ${title} +`// ${title} // DO NOT EDIT THIS FILE! // Source: /${src} ${text}`, 'utf-8'); - }; -const modules = [ +const files = [ { src : 'src/main/ua-parser.js', dest : 'src/main/ua-parser.mjs', - title : 'ua-parser-js', + title : 'Generated ESM version of ua-parser-js', replacements : [ [/\(func[\s\S]+strict\';/ig, ''], [/esversion\: 3/ig, 'esversion: 6'], - [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'] + [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'], + ...defaultReplacements.mjs ] - },{ + }, + { src : 'src/enums/ua-parser-enums.js', dest :'src/enums/ua-parser-enums.mjs', - title : 'ua-parser-js/enums', - replacements : [] + title : 'Generated ESM version of ua-parser-js/enums', + replacements : [...defaultReplacements.mjs] + }, + { + src : 'src/enums/ua-parser-enums.js', + dest :'src/enums/ua-parser-enums.d.ts', + title : 'Generated type declarations of ua-parser-js/enums', + replacements : [ + [/(const .+) = object\.freeze\(/ig, 'export $1: Readonly<'], + [/(const .+) =( .+;)/ig, 'export $1: typeof$2'], + [/}\);/ig, '}>;'], + [/module\.exports =.+/igs, ''] + ] }, { src : 'src/extensions/ua-parser-extensions.js', dest : 'src/extensions/ua-parser-extensions.mjs', - title : 'ua-parser-js/extensions', - replacements : [] + title : 'Generated ESM version of ua-parser-js/extensions', + replacements : [...defaultReplacements.mjs] }, { src : 'src/helpers/ua-parser-helpers.js', dest : 'src/helpers/ua-parser-helpers.mjs', - title : 'ua-parser-js/helpers', - replacements : [] + title : 'Generated ESM version of ua-parser-js/helpers', + replacements : [...defaultReplacements.mjs] } ]; -modules.forEach(module => generateMJS(module)); \ No newline at end of file +files.forEach(module => generateFile(module)); \ No newline at end of file diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts index 5d25f94..b748ea9 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -1,160 +1,171 @@ -// Type definitions for Enums submodule of UAParser.js v2.0.4 -// Project: https://github.com/faisalman/ua-parser-js -// Definitions by: Faisal Salman +// Generated type declarations of ua-parser-js/enums +// DO NOT EDIT THIS FILE! +// Source: /src/enums/ua-parser-enums.js + +/////////////////////////////////////////////// +/* Enums for UAParser.js v2.0.4 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ export const BrowserName: 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", - DAUM: "Daum", - DILLO: "Dillo", - DOLPHIN: "Dolphin", + '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', + DAUM: 'Daum', + DILLO: 'Dillo', + DOLPHIN: 'Dolphin', DOOBLE: 'Dooble', - DORIS: "Doris", - DRAGON: "Dragon", - DUCKDUCKGO: "DuckDuckGo", - ECOSIA: "Ecosia", - EDGE: "Edge", - EDGE_WEBVIEW: "Edge WebView", - EDGE_WEBVIEW2: "Edge WebView2", - 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", - LG: "LG Browser", - 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_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", - PHOENIX: "Phoenix", - PICOBROWSER: "Pico Browser", - POLARIS: "Polaris", - PUFFIN: "Puffin", - QQ: "QQBrowser", - QQ_LITE: "QQBrowserLite", - QUARK: "Quark", - QUPZILLA: "QupZilla", - QUTEBROWSER: "qutebrowser", - 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", - SURF: "Surf", - 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", - ZALO: "Zalo", + DORIS: 'Doris', + DRAGON: 'Dragon', + DUCKDUCKGO: 'DuckDuckGo', + ECOSIA: 'Ecosia', + EDGE: 'Edge', + EDGE_WEBVIEW: 'Edge WebView', + EDGE_WEBVIEW2: 'Edge WebView2', + 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', + LG: 'LG Browser', + 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_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', + PHOENIX: 'Phoenix', + PICOBROWSER: 'Pico Browser', + POLARIS: 'Polaris', + PUFFIN: 'Puffin', + QQ: 'QQBrowser', + QQ_LITE: 'QQBrowserLite', + QUARK: 'Quark', + QUPZILLA: 'QupZilla', + QUTEBROWSER: 'qutebrowser', + 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', + SURF: 'Surf', + 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', + ZALO: 'Zalo' + + // TODO : test! }>; /** * @deprecated Use `BrowserName` instead @@ -162,34 +173,34 @@ export const BrowserName: Readonly<{ export const Browser: typeof BrowserName; export const BrowserType: Readonly<{ - CRAWLER: "crawler", - CLI: "cli", - EMAIL: "email", - FETCHER: "fetcher", - INAPP: "inapp", - MEDIAPLAYER: "mediaplayer", - LIBRARY: "library", + CRAWLER: 'crawler', + CLI: 'cli', + EMAIL: 'email', + FETCHER: 'fetcher', + INAPP: 'inapp', + MEDIAPLAYER: 'mediaplayer', + LIBRARY: 'library' }>; export const CPUArch: Readonly<{ - '68K': "68k", - ALPHA: "alpha", - 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", + '68K': '68k', + ALPHA: 'alpha', + 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' }>; /** * @deprecated Use `CPUArch` instead @@ -197,14 +208,14 @@ export const CPUArch: Readonly<{ export const CPU: typeof CPUArch; export const DeviceType: Readonly<{ - CONSOLE: "console", - DESKTOP: "desktop", - EMBEDDED: "embedded", - MOBILE: "mobile", - SMARTTV: "smarttv", - TABLET: "tablet", - WEARABLE: "wearable", - XR: "xr" + CONSOLE: 'console', + DESKTOP: 'desktop', + EMBEDDED: 'embedded', + MOBILE: 'mobile', + SMARTTV: 'smarttv', + TABLET: 'tablet', + WEARABLE: 'wearable', + XR: 'xr' }>; /** * @deprecated Use `DeviceType` instead @@ -212,78 +223,80 @@ export const DeviceType: Readonly<{ export const Device: typeof DeviceType; export const DeviceVendor: Readonly<{ - ACER: "Acer", - ADVAN: "Advan", - ALCATEL: "Alcatel", - APPLE: "Apple", - AMAZON: "Amazon", - ARCHOS: "Archos", - ASUS: "ASUS", - ATT: "AT&T", - BENQ: "BenQ", - BLACKBERRY: "BlackBerry", - BLU: "BLU", - 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", - LAVA: "Lava", - 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", - PHILIPS: "Philips", - PICO: "Pico", - POLYTRON: "Polytron", - REALME: "Realme", - RETROID: "Retroid", - 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", - VIZIO: "Vizio", - VODAFONE: "Vodafone", - XBOX: "Xbox", - XIAOMI: "Xiaomi", - ZEBRA: "Zebra", - ZTE: "ZTE", + ACER: 'Acer', + ADVAN: 'Advan', + ALCATEL: 'Alcatel', + APPLE: 'Apple', + AMAZON: 'Amazon', + ARCHOS: 'Archos', + ASUS: 'ASUS', + ATT: 'AT&T', + BENQ: 'BenQ', + BLACKBERRY: 'BlackBerry', + BLU: 'BLU', + 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', + LAVA: 'Lava', + 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', + PHILIPS: 'Philips', + PICO: 'Pico', + POLYTRON: 'Polytron', + REALME: 'Realme', + RETROID: 'Retroid', + 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', + VIZIO: 'Vizio', + VODAFONE: 'Vodafone', + XBOX: 'Xbox', + XIAOMI: 'Xiaomi', + ZEBRA: 'Zebra', + ZTE: 'ZTE', + + // TODO : test! }>; /** * @deprecated Use `DeviceVendor` instead @@ -291,26 +304,26 @@ export const DeviceVendor: Readonly<{ export const Vendor: typeof DeviceVendor; export const EngineName: 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", + 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' }>; /** * @deprecated Use `EngineName` instead @@ -318,98 +331,100 @@ export const EngineName: Readonly<{ export const Engine: typeof EngineName; export const OSName: Readonly<{ - AIX: "AIX", - AMIGA_OS: "Amiga OS", - ANDROID: "Android", - ANDROID_X86: "Android-x86", - ARCAOS: "ArcaOS", - 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", - KNOPPIX: "Knoppix", - 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", - UBUNTU_TOUCH: "Ubuntu Touch", - UNIX: "Unix", - VECTORLINUX: "VectorLinux", - 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", + AIX: 'AIX', + AMIGA_OS: 'Amiga OS', + ANDROID: 'Android', + ANDROID_X86: 'Android-x86', + ARCAOS: 'ArcaOS', + 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', + KNOPPIX: 'Knoppix', + 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', + UBUNTU_TOUCH: 'Ubuntu Touch', + UNIX: 'Unix', + VECTORLINUX: 'VectorLinux', + 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! }>; /** * @deprecated Use `OSName` instead @@ -421,7 +436,7 @@ export const OS: typeof OSName; */////////////////////////////// export const Extension: Readonly<{ - Browser: { + BrowserName: { CLIs: { CURL: 'curl', ELINKS: 'ELinks', @@ -441,6 +456,9 @@ export const Extension: Readonly<{ AMAZON_BOT: 'Amazonbot', AMAZON_CONTXBOT: 'contxbot', ANTHROPIC_AI: 'anthropic-ai', + ANTHROPIC_CLAUDE_BOT: 'ClaudeBot', + ANTHROPIC_CLAUDE_SEARCHBOT: 'Claude-SearchBot', + ANTHROPIC_CLAUDE_WEB: 'Claude-Web', ARCHIVEORG_BOT: 'archive.org_bot', BAIDU_ADS: 'Baidu-ADS', BAIDU_SPIDER: 'Baiduspider', @@ -457,8 +475,6 @@ export const Extension: Readonly<{ BYTEDANCE_SPIDER: 'Bytespider', CC_BOT: 'CCBot', CHATGLM_SPIDER: 'ChatGLM-Spider', - CLAUDE_WEB: 'Claude-Web', - CLAUDE_BOT: 'ClaudeBot', COCCOC_BOT_WEB: 'coccocbot-web', COCCOC_BOT_IMAGE: 'coccocbot-image', COHERE_TRAINING_DATA_CRAWLER: 'cohere-training-data-crawler', @@ -573,11 +589,13 @@ export const Extension: Readonly<{ }, Fetchers: { AHREFS_SITEAUDIT: 'AhrefsSiteAudit', + ANTHROPIC_CLAUDE_USER: 'Claude-User', ASANA: 'Asana', BETTER_UPTIME_BOT: 'Better Uptime Bot', BITLY_BOT: 'bitlybot', BLUESKY: 'Bluesky', BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot', + COHERE_AI: 'Cohere-AI', DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot', GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', @@ -666,4 +684,5 @@ export const Extension: Readonly<{ VOLVO: 'Volvo' } } -}>; \ No newline at end of file +}>; + diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 94b1f12..bb3b83c 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -452,6 +452,9 @@ const Extension = Object.freeze({ AMAZON_BOT: 'Amazonbot', AMAZON_CONTXBOT: 'contxbot', ANTHROPIC_AI: 'anthropic-ai', + ANTHROPIC_CLAUDE_BOT: 'ClaudeBot', + ANTHROPIC_CLAUDE_SEARCHBOT: 'Claude-SearchBot', + ANTHROPIC_CLAUDE_WEB: 'Claude-Web', ARCHIVEORG_BOT: 'archive.org_bot', BAIDU_ADS: 'Baidu-ADS', BAIDU_SPIDER: 'Baiduspider', @@ -468,8 +471,6 @@ const Extension = Object.freeze({ BYTEDANCE_SPIDER: 'Bytespider', CC_BOT: 'CCBot', CHATGLM_SPIDER: 'ChatGLM-Spider', - CLAUDE_WEB: 'Claude-Web', - CLAUDE_BOT: 'ClaudeBot', COCCOC_BOT_WEB: 'coccocbot-web', COCCOC_BOT_IMAGE: 'coccocbot-image', COHERE_TRAINING_DATA_CRAWLER: 'cohere-training-data-crawler', @@ -584,11 +585,13 @@ const Extension = Object.freeze({ }, Fetchers: { AHREFS_SITEAUDIT: 'AhrefsSiteAudit', + ANTHROPIC_CLAUDE_USER: 'Claude-User', ASANA: 'Asana', BETTER_UPTIME_BOT: 'Better Uptime Bot', BITLY_BOT: 'bitlybot', BLUESKY: 'Bluesky', BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot', + COHERE_AI: 'Cohere-AI', DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot', GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 6346b9f..3d6c9fc 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -250,7 +250,7 @@ const Emails = Object.freeze({ const Fetchers = Object.freeze({ browser : [ [ - // Asana / Bitlybot / Better Uptime / BingPreview / Blueno / HubSpot Page Fetcher / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot + // Asana / Bitlybot / Better Uptime / BingPreview / Blueno / Cohere-AI / HubSpot Page Fetcher / 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 @@ -260,7 +260,7 @@ const Fetchers = Object.freeze({ // Perplexity-User - https://docs.perplexity.ai/guides/bots // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|hubspot page fetcher|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, + /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|hubspot page fetcher|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, @@ -277,8 +277,8 @@ const Fetchers = Object.freeze({ [NAME, VERSION, [TYPE, FETCHER]], [ - // 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|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i + // Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / Snapchat / TikTokSpider / Vercelbot / Yandex Bots + /((?:better uptime |telegram|vercel)bot|chrome-lighthouse|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.d.ts b/src/helpers/ua-parser-helpers.d.ts index fc53c61..c860fd2 100644 --- a/src/helpers/ua-parser-helpers.d.ts +++ b/src/helpers/ua-parser-helpers.d.ts @@ -4,24 +4,12 @@ import type { 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; -declare function isFromEU(): boolean; -declare function isFrozenUA(ua: string): boolean; -declare function isStandalonePWA(): boolean; - -export { - getDeviceVendor, - isAppleSilicon, - isAIBot, - isBot, - isChromeFamily, - isElectron, - isFromEU, - isFrozenUA, - isStandalonePWA -} \ No newline at end of file +export function getDeviceVendor(model: string): string | undefined; +export function isAppleSilicon(resultOrUA: IResult | string): boolean; +export function isAIBot(resultOrUA: IResult | string): boolean; +export function isBot(resultOrUA: IResult | string): boolean; +export function isChromeFamily(resultOrUA: IResult | string): boolean; +export function isElectron(): boolean; +export function isFromEU(): boolean; +export function isFrozenUA(ua: string): boolean; +export function isStandalonePWA(): boolean; diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 912ccdb..6626ad5 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -8,7 +8,7 @@ /*jshint esversion: 6 */ const { UAParser } = require('../main/ua-parser'); -const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); +const { CPUArch, OSName, EngineName } = require('../enums/ua-parser-enums'); const { Bots } = require('../extensions/ua-parser-extensions'); const { isFromEU } = require('detect-europe-js'); const { isFrozenUA } = require('ua-is-frozen'); @@ -20,8 +20,8 @@ const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${m const isAppleSilicon = (resultOrUA) => { const res = toResult(resultOrUA); - if (res.os.is(OS.MACOS)) { - if (res.cpu.is(CPU.ARM)) { + if (res.os.is(OSName.MACOS)) { + if (res.cpu.is(CPUArch.ARM)) { return true; } if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') { @@ -164,7 +164,7 @@ const isBot = (resultOrUA) => [ 'library' ].includes(toResult(resultOrUA, Bots).browser.type); -const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(Engine.BLINK); +const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(EngineName.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js / electron\//i.test(navigator?.userAgent)); // browser diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 9d49448..6fd0ef5 100644 --- a/src/main/ua-parser.d.ts +++ b/src/main/ua-parser.d.ts @@ -3,7 +3,7 @@ // Definitions by: Faisal Salman import type { Headers } from "undici"; -import { BrowserType, CPU as CPUArch, Device as DeviceType, Engine as EngineName } from "../enums/ua-parser-enums"; +import { BrowserType, CPUArch, DeviceType, EngineName } from "../enums/ua-parser-enums"; declare namespace UAParser { diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 3506077..61d6a21 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -329,6 +329,16 @@ "type" : "crawler" } }, + { + "desc" : "Claude-SearchBot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Claude-SearchBot/1.0; +Claude-SearchBot@anthropic.com)", + "expect" : + { + "name" : "Claude-SearchBot", + "version" : "1.0", + "type" : "crawler" + } + }, { "desc" : "ClaudeWeb", "ua" : "Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index c1c8989..33df886 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -99,6 +99,26 @@ "type" : "fetcher" } }, + { + "desc" : "Claude-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Claude-User/1.0; +Claude-User@anthropic.com)", + "expect" : + { + "name" : "Claude-User", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Cohere-AI", + "ua" : "Mozilla/5.0 (compatible; Cohere-AI/1.0; +https://cohere.com/)", + "expect" : + { + "name" : "Cohere-AI", + "version" : "1.0", + "type" : "fetcher" + } + }, { "desc" : "DuckAssistBot", "ua" : "DuckAssistBot/1.2; (+http://duckduckgo.com/duckassistbot.html)", diff --git a/test/unit/es6.mjs b/test/unit/es6.mjs index 8e26908..9f3ba26 100644 --- a/test/unit/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 { CPUArch, DeviceType, EngineName } from '../../src/enums/ua-parser-enums.mjs'; import * as assert from 'assert'; describe('Returns', () => { @@ -19,8 +19,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(CPU.ARM), true); - assert.strictEqual(device.is(Device.MOBILE), true); - assert.strictEqual(engine.is(Engine.GECKO), true); + 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 ce242a362fcf5b6a4fd01ddec39174e055c1bec2 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sat, 30 Aug 2025 17:01:05 +0700 Subject: [PATCH 24/30] [extensions][enums] Improve detection for Yandex bots --- src/enums/ua-parser-enums.js | 37 ++++ src/extensions/ua-parser-extensions.js | 11 +- test/data/ua/extension/crawler.json | 280 +++++++++++++++++++++++++ test/data/ua/extension/fetcher.json | 90 ++++++++ 4 files changed, 415 insertions(+), 3 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index bb3b83c..d06e1db 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -553,7 +553,35 @@ const Extension = Object.freeze({ VERCEL_V0BOT: 'v0bot', YAHOO_JAPAN: 'Y!J-BRW', YAHOO_SLURP: 'Yahoo! Slurp', + YANDEX_ACCESSIBILITY_BOT: 'YandexAccessibilityBot', + YANDEX_ADDITIONAL_BOT: 'YandexAdditionalBot', + YANDEX_ADNET: 'YandexAdNet', + YANDEX_BLOGS: 'YandexBlogs', YANDEX_BOT: 'YandexBot', + YANDEX_BOT_MIRRORDETECTOR: 'YandexBot MirrorDetector', + YANDEX_COMBOT: 'YandexComBot', + YANDEX_FAVICONS: 'YandexFavicons', + YANDEX_IMAGE_RESIZER: 'YandexImageResizer', + YANDEX_IMAGES: 'YandexImages', + YANDEX_MARKET: 'YandexMarket', + YANDEX_MEDIA: 'YandexMedia', + YANDEX_METRIKA: 'YandexMetrika', + YANDEX_MOBILE_BOT: 'YandexMobileBot', + YANDEX_MOBILE_SCREENSHOT_BOT: 'YandexMobileScreenShotBot', + YANDEX_NEWS: 'YandexNews', + YANDEX_ONTODB: 'YandexOntoDB', + YANDEX_ONTODB_API: 'YandexOntoDBAPI', + YANDEX_PARTNER: 'YandexPartner', + YANDEX_RCA: 'YandexRCA', + YANDEX_RENDERRESOURCES_BOT: 'YandexRenderResourcesBot', + YANDEX_SCREENSHOT_BOT: 'YandexScreenshotBot', + YANDEX_SPRAV_BOT: 'YandexSpravBot', + YANDEX_TRACKER: 'YandexTracker', + YANDEX_VERTICALS: 'YandexVerticals', + YANDEX_VERTIS: 'YandexVertis', + YANDEX_VIDEO: 'YandexVideo', + YANDEX_VIDEO_PARSER: 'YandexVideoParser', + YANDEX_WEBMASTER: 'YandexWebmaster', YEP_BOT: 'YepBot', YETI: 'Yeti', YISOU_SPIDER: 'YisouSpider', @@ -624,6 +652,15 @@ const Extension = Object.freeze({ VERCEL_BOT: 'Vercelbot', VERCEL_FLAGS: 'vercelflags', VERCEL_TRACING: 'verceltracing', + YANDEX_CALENDAR: 'YandexCalendar', + YANDEX_DIRECT: 'YandexDirect', + YANDEX_DIRECTDYN: 'YandexDirectDyn', + YANDEX_DIRECTFETCHER: 'YaDirectFetcher', + YANDEX_FORDOMAIN: 'YandexForDomain', + YANDEX_PAGECHECKER: 'YandexPagechecker', + YANDEX_SEARCHSHOP: 'YandexSearchShop', + YANDEX_SITELINKS: 'YandexSitelinks', + YANDEX_USERPROXY: 'YandexUserproxy', WHATSAPP: 'WhatsApp', ZOOMINFO_BOT: 'Zoombot' }, diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 3d6c9fc..5f345e1 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -109,7 +109,7 @@ 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|com|renderresources|screenshot|sprav)?bot(?!.+mirror)|image(?:s|resizer)|adnet|blogs|favicons|market|media|metrika|news|ontodb(?:api)?|partner|rca|tracker|turbo|verti(?:cal)?s|webmaster|video(?:parser)?))\/([\w\.]+)/i, // Yeti (Naver) /(yeti)\/([\w\.]+)/i, @@ -119,9 +119,14 @@ const Crawlers = Object.freeze({ // Freespoke - https://docs.freespoke.com/search/bot/ /((?:aihit|blex|diff|huggingface-|msn|pangu|replicate-|runpod-|timpi|together-|xai-|you|zum)bot|(?:magpie-|velenpublicweb)crawler|(?:chatglm-|line|screaming frog seo |yisou)spider|cotoyogi|firecrawlagent|freespoke|omgili(?:bot)?|openai image downloader|startpageprivateimageproxy|twinagent|webzio-extended)\/?([\w\.]*)/i ], - [NAME, VERSION, [TYPE, CRAWLER]], + [ + // YandexBot MirrorDetector + /(yandexbot\/([\w\.]+); mirrordetector)/i + ], + [[NAME, /\/.+;/ig, ''], VERSION, [TYPE, CRAWLER]], + [ // Google Bots /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, @@ -260,7 +265,7 @@ const Fetchers = Object.freeze({ // Perplexity-User - https://docs.perplexity.ai/guides/bots // MistralAI-User - https://docs.mistral.ai/robots/ // Yandex Bots - https://yandex.com/bots - /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|hubspot page fetcher|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, + /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|hubspot page fetcher|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)?|fordomain|pagechecker|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 61d6a21..312831e 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -1170,6 +1170,46 @@ "type" : "crawler" } }, + { + "desc" : "YandexAccessibilityBot", + "ua" : "Mozilla/5.0 (compatible; YandexAccessibilityBot/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexAccessibilityBot", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexAdditionalBot", + "ua" : "Mozilla/5.0 (compatible; YandexAdditionalBot/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexAdditionalBot", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexAdNet", + "ua" : "Mozilla/5.0 (compatible; YandexAdNet/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexAdNet", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexBlogs", + "ua" : "Mozilla/5.0 (compatible; YandexBlogs/0.99; robot; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexBlogs", + "version" : "0.99", + "type" : "crawler" + } + }, { "desc" : "YandexBot", "ua" : "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)", @@ -1180,6 +1220,246 @@ "type" : "crawler" } }, + { + "desc" : "YandexBot MirrorDetector", + "ua" : "Mozilla/5.0 (compatible; YandexBot/3.0; MirrorDetector; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexBot MirrorDetector", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexComBot", + "ua" : "Mozilla/5.0 (compatible; YandexComBot/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexComBot", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexFavicons", + "ua" : "Mozilla/5.0 (compatible; YandexFavicons/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexFavicons", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexImageResizer", + "ua" : "Mozilla/5.0 (compatible; YandexImageResizer/2.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexImageResizer", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexImages", + "ua" : "Mozilla/5.0 (compatible; YandexImages/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexImages", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexMarket", + "ua" : "Mozilla/5.0 (compatible; YandexMarket/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexMarket", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexMetrika", + "ua" : "Mozilla/5.0 (compatible; YandexMetrika/2.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexMetrika", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexMedia", + "ua" : "Mozilla/5.0 (compatible; YandexMedia/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexMedia", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexMobileBot", + "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/12B411 Safari/600.1.4 (compatible; YandexMobileBot/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexMobileBot", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexMobileScreenShotBot", + "ua" : "Mozilla/5.0 (compatible; YandexMobileScreenShotBot/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexMobileScreenShotBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexNews", + "ua" : "Mozilla/5.0 (compatible; YandexNews/4.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexNews", + "version" : "4.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexOntoDB", + "ua" : "Mozilla/5.0 (compatible; YandexOntoDB/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexOntoDB", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexOntoDBAPI", + "ua" : "Mozilla/5.0 (compatible; YandexOntoDBAPI/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexOntoDBAPI", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexPartner", + "ua" : "Mozilla/5.0 (compatible; YandexPartner/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexPartner", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexRCA", + "ua" : "Mozilla/5.0 (compatible; YandexRCA/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexRCA", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexRenderResourcesBot", + "ua" : "Mozilla/5.0 (compatible; YandexRenderResourcesBot/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexRenderResourcesBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexScreenshotBot", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Safari/537.36 (compatible; YandexScreenshotBot/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexScreenshotBot", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexSpravBot", + "ua" : "Mozilla/5.0 (compatible; YandexSpravBot/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexSpravBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexTracker", + "ua" : "Mozilla/5.0 (compatible; YandexTracker/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexTracker", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexVertis", + "ua" : "Mozilla/5.0 (compatible; YandexVertis/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexVertis", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexVerticals", + "ua" : "Mozilla/5.0 (compatible; YandexVerticals/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexVerticals", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexVideo", + "ua" : "Mozilla/5.0 (compatible; YandexVideo/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexVideo", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexVideoParser", + "ua" : "Mozilla/5.0 (compatible; YandexVideoParser/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexVideoParser", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "YandexWebmaster", + "ua" : "Mozilla/5.0 (compatible; YandexWebmaster/2.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexWebmaster", + "version" : "2.0", + "type" : "crawler" + } + }, { "desc" : "YepBot", "ua" : "Mozilla/5.0 (compatible; YepBot/1.0; +http://yep.com/yepbot/)", diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 33df886..7b0bd4e 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -419,6 +419,96 @@ "type" : "fetcher" } }, + { + "desc" : "YaDirectFetcher", + "ua" : "Mozilla/5.0 (compatible; YaDirectFetcher/1.0; Dyatel; +http://yandex.com/bots)", + "expect" : + { + "name" : "YaDirectFetcher", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "YandexCalendar", + "ua" : "Mozilla/5.0 (compatible; YandexCalendar/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexCalendar", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "YandexDirect", + "ua" : "Mozilla/5.0 (compatible; YandexDirect/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexDirect", + "version" : "3.0", + "type" : "fetcher" + } + }, + { + "desc" : "YandexDirectDyn", + "ua" : "Mozilla/5.0 (compatible; YandexDirectDyn/1.0; +http://yandex.com/bots", + "expect" : + { + "name" : "YandexDirectDyn", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "YandexForDomain", + "ua" : "Mozilla/5.0 (compatible; YandexForDomain/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexForDomain", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "YandexPagechecker", + "ua" : "Mozilla/5.0 (compatible; YandexPagechecker/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexPagechecker", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "YandexSearchShop", + "ua" : "Mozilla/5.0 (compatible; YandexSearchShop/1.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexSearchShop", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "YandexSitelinks", + "ua" : "Mozilla/5.0 (compatible; YandexSitelinks; Dyatel; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexSitelinks", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "YandexUserproxy", + "ua" : "Mozilla/5.0 (compatible; YandexUserproxy; robot; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexUserproxy", + "version" : "undefined", + "type" : "fetcher" + } + }, { "desc" : "Zoombot", "ua" : "Mozilla/5.0 (compatible; Zoombot/1.0; +https://zoom.us; crawler@domain.com)", From 146f182533cad0f2f2303c6e9aa98db923e653f3 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 31 Aug 2025 20:04:49 +0700 Subject: [PATCH 25/30] [extensions] Improve bot detection for ByteDance, Google, SB Intuitions, Webzio --- src/enums/ua-parser-enums.js | 20 ++++++--- src/extensions/ua-parser-extensions.js | 7 +-- src/helpers/ua-parser-helpers.js | 4 ++ test/data/ua/extension/crawler.json | 60 ++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index d06e1db..7d5eb6d 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -441,7 +441,6 @@ const Extension = Object.freeze({ WGET: 'wget' }, Crawlers: { - '360_SPIDER': '360Spider', AHREFS_BOT: 'AhrefsBot', AI2_BOT: 'AI2Bot', AIHIT_BOT: 'aiHitBot', @@ -468,9 +467,9 @@ const Extension = Object.freeze({ BLEX_BOT: 'BLEXBot', BOTIFY: 'botify', BRAVE_BOT: 'Bravebot', - BYTEDANCE_SPIDER: 'Bytespider', + BYTEDANCE_BYTESPIDER: 'Bytespider', + BYTEDANCE_TIKTOKSPIDER: 'TikTokSpider', CC_BOT: 'CCBot', - CHATGLM_SPIDER: 'ChatGLM-Spider', COCCOC_BOT_WEB: 'coccocbot-web', COCCOC_BOT_IMAGE: 'coccocbot-image', COHERE_TRAINING_DATA_CRAWLER: 'cohere-training-data-crawler', @@ -492,10 +491,12 @@ const Extension = Object.freeze({ GOOGLE_ADSBOT: 'AdsBot-Google', GOOGLE_ADSBOT_MOBILE: 'Adsbot-Google-Mobile', GOOGLE_ADSENSE: 'AdSense', + GOOGLE_APIS: 'APIs-Google', GOOGLE_BOT: 'Googlebot', GOOGLE_BOT_IMAGE: 'Googlebot-Image', GOOGLE_BOT_NEWS: 'Googlebot-News', GOOGLE_BOT_VIDEO: 'Googlebot-Video', + GOOGLE_CLOUDVERTEXBOT: 'Google-CloudVertexBot', GOOGLE_INSPECTIONTOOL: 'Google-InspectionTool', GOOGLE_OTHER: 'GoogleOther', GOOGLE_OTHER_IMAGE: 'GoogleOther-Image', @@ -525,16 +526,16 @@ const Extension = Object.freeze({ MICROSOFT_ADIDXBOT: 'adidxbot', MOJEEK_BOT: 'MojeekBot', MOZ_DOTBOT: 'DotBot', - OMGILI: 'omgili', - OMGILI_BOT: 'omgilibot', ONCRAWL: 'OnCrawl', ONESPOT_SCRAPERBOT: 'Onespot-ScraperBot', OPENAI_GPTBOT: 'GPTBot', OPENAI_SEARCH: 'OAI-SearchBot', PERPLEXITY_BOT: 'PerplexityBot', + QIHOO_360_SPIDER: '360Spider', QWANT_BOT: 'Qwantbot', REPLICATE_BOT: 'Replicate-Bot', RUNPOD_BOT: 'RunPod-Bot', + SB_INTUITIONS_BOT: 'SBIntuitionsBot', SEEKPORT_BOT: 'SeekportBot', SEMRUSH_BOT: 'SemrushBot', SEMRUSH_BOT_BACKLINK: 'SemrushBot-BA', @@ -549,8 +550,12 @@ const Extension = Object.freeze({ TOGETHER_BOT: 'Together-Bot', TURNITIN_BOT: 'TurnitinBot', TWIN_AGENT: 'TwinAgent', - XAI_BOT: 'xAI-Bot', VERCEL_V0BOT: 'v0bot', + WEBZIO: 'webzio', + WEBZIO_EXTENDED: 'Webzio-Extended', + WEBZIO_OMGILI: 'omgili', + WEBZIO_OMGILI_BOT: 'omgilibot', + XAI_BOT: 'xAI-Bot', YAHOO_JAPAN: 'Y!J-BRW', YAHOO_SLURP: 'Yahoo! Slurp', YANDEX_ACCESSIBILITY_BOT: 'YandexAccessibilityBot', @@ -586,6 +591,7 @@ const Extension = Object.freeze({ YETI: 'Yeti', YISOU_SPIDER: 'YisouSpider', YOU_BOT: 'YouBot', + ZHIPU_CHATGLM_SPIDER: 'ChatGLM-Spider', ZUM_BOT: 'ZumBot' }, Emails: { @@ -624,7 +630,7 @@ const Extension = Object.freeze({ GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', GOOGLE_GEMINI_DEEP_RESEARCH: 'Gemini-Deep-Research', - GOOGLE_IMAGE_PROXY: 'GoogleImageProxy', + GOOGLE_IMAGEPROXY: 'GoogleImageProxy', GOOGLE_PAGERENDERER: 'Google-PageRenderer', GOOGLE_READ_ALOUD: 'Google-Read-Aloud', GOOGLE_PRODUCER: 'GoogleProducer', diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js index 5f345e1..cc6f8d0 100644 --- a/src/extensions/ua-parser-extensions.js +++ b/src/extensions/ua-parser-extensions.js @@ -61,9 +61,10 @@ const Crawlers = Object.freeze({ // Onespot - https://www.onespot.com/identifying-traffic.html // OpenAI's SearchGPT - https://platform.openai.com/docs/bots // PerplexityBot - https://perplexity.ai/perplexitybot + // SBIntuitionsBot - https://www.sbintuitions.co.jp/bot/ // SeznamBot - http://napoveda.seznam.cz/seznambot-intro // YepBot - https://yep.com/yepbot/ - /((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam|yep)bot)\/([\w\.-]+)/i, + /((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|sbintuitions|semrush|seznam|yep)bot)\/([\w\.-]+)/i, // Algolia Crawler /(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i, @@ -139,8 +140,8 @@ const Crawlers = Object.freeze({ // 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 - // Botify / Bytespider / DeepSeekBot / Qihoo 360Spider / SeekportBot - /\b((?:ai2|aspiegel|dataforseo|deepseek|imagesift|petal|seekport|turnitin|v0)bot|360spider-?(?:image|video)?|baidu-ads|botify|bytespider|cohere-training-data-crawler|elastic(?=\/s)|marginalia|siteimprove(?=bot|\.com)|teoma|yahoo! slurp)/i + // Botify / Bytespider / DeepSeekBot / Qihoo 360Spider / SeekportBot / TikTokSpider + /\b((ai2|aspiegel|dataforseo|deepseek|imagesift|petal|seekport|turnitin|v0)bot|360spider-?(image|video)?|baidu-ads|botify|(byte|tiktok)spider|cohere-training-data-crawler|elastic(?=\/s)|marginalia|siteimprove(?=bot|\.com)|teoma|webzio|yahoo! slurp)/i ], [NAME, [TYPE, CRAWLER]] ] diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index 6626ad5..e8af693 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -88,6 +88,7 @@ const isAIBot = (resultOrUA) => [ 'googleother', 'googleother-image', 'googleother-video', + 'google-cloudvertexbot', 'google-extended', // Hive AI @@ -123,6 +124,9 @@ const isAIBot = (resultOrUA) => [ // Runpod 'runpod-bot', + // SB Intuitions + 'sbintuitionsbot', + // Semrush 'semrushbot-ocob', diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json index 312831e..d00a108 100644 --- a/test/data/ua/extension/crawler.json +++ b/test/data/ua/extension/crawler.json @@ -579,6 +579,16 @@ "type" : "crawler" } }, + { + "desc" : "APIs-Google", + "ua" : "APIs-Google (+https://developers.google.com/webmasters/APIs-Google.html)", + "expect" : + { + "name" : "APIs-Google", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Googlebot-Video", "ua" : "Googlebot-Video/1.0", @@ -679,6 +689,16 @@ "type" : "crawler" } }, + { + "desc" : "Google-CloudVertexBot", + "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.7204.183 Mobile Safari/537.36 (compatible; Google-CloudVertexBot; +https://cloud.google.com/enterprise-search)", + "expect" : + { + "name" : "Google-CloudVertexBot", + "version" : "undefined", + "type" : "crawler" + } + }, { "desc" : "Google-Safety", "ua" : "Google-Safety", @@ -970,6 +990,16 @@ "type" : "crawler" } }, + { + "desc" : "SBIntuitionsBot", + "ua" : "Mozilla/5.0 (compatible; SBIntuitionsBot/0.1;+https://www.sbintuitions.co.jp/bot/)", + "expect" : + { + "name" : "SBIntuitionsBot", + "version" : "0.1", + "type" : "crawler" + } + }, { "desc" : "SeekportBot", "ua" : "Mozilla/5.0 (compatible; SeekportBot; +https://bot.seekport.com)", @@ -1080,6 +1110,16 @@ "type" : "crawler" } }, + { + "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" : "crawler" + } + }, { "desc" : "Timpibot", "ua" : "Timpibot/0.8 (+http://www.timpi.io)", @@ -1150,6 +1190,26 @@ "type" : "crawler" } }, + { + "desc" : "webzio", + "ua" : "webzio (+https://webz.io/bot.html)", + "expect" : + { + "name" : "webzio", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Webzio-Extended", + "ua" : "Mozilla/5.0 (compatible; Webzio-Extended/1.0; +https://www.webzio.com/bot.html)", + "expect" : + { + "name" : "Webzio-Extended", + "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)", From b1d9dcafcdd86423dc70050a56aee02aa971e957 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Mon, 1 Sep 2025 23:10:22 +0700 Subject: [PATCH 26/30] [test] Move UA-CH test data into its own file --- test/data/ua-ch/headers.js | 242 +++++++++++++++++++++++++++++++++++++ test/unit/ua-ch.js | 232 +---------------------------------- 2 files changed, 248 insertions(+), 226 deletions(-) create mode 100644 test/data/ua-ch/headers.js diff --git a/test/data/ua-ch/headers.js b/test/data/ua-ch/headers.js new file mode 100644 index 0000000..77aa67d --- /dev/null +++ b/test/data/ua-ch/headers.js @@ -0,0 +1,242 @@ +const UACHTests = [ + { + desc: 'Avast Secure Browser', + headers : { + 'sec-ch-ua': '"Avast Secure Browser";v="131", "Chromium";v="131", "Not_A Brand";v="24"' + }, + expect: { + browser : { + name : 'Avast Secure Browser', + version : '131', + major : '131', + type : undefined + } + } + }, + { + desc: 'Brave', + headers : { + 'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Brave";v="132"' + }, + expect: { + browser : { + name : 'Brave', + version : '132', + major : '132', + type : undefined + } + } + }, + { + desc: 'Chrome', + headers : { + 'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"' + }, + expect: { + browser : { + name : 'Chrome', + version : '111', + major : '111', + type : undefined + } + } + }, + { + desc: 'Chrome Headless', + headers : { + 'sec-ch-ua': '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"' + }, + expect: { + browser : { + name : 'Chrome Headless', + version : '124', + major : '124', + type : undefined + } + } + }, + { + desc: 'Chrome WebView', + 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 + } + } + }, + { + desc: 'DuckDuckGo', + 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 + } + } + }, + { + desc: 'Edge', + 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 + } + } + }, + { + desc: 'Edge WebView2', + 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 + } + } + }, + { + desc: 'Huawei Browser', + 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 + } + } + }, + { + desc: 'MIUI Browser', + 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 + } + } + }, + { + desc: 'Oculus Browser', + 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 + } + } + }, + { + desc: 'Opera', + 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 + } + } + }, + { + desc: 'Opera GX', + 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 + } + } + }, + { + desc: 'Opera Mobi', + 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 + } + } + }, + { + desc: 'Opera Mobi', + 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 + } + } + }, + { + desc: 'Samsung Internet', + 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 + } + } + }, + { + desc: 'Yandex', + 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 + } + } + } +]; + +module.exports = UACHTests; \ No newline at end of file diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index 7d12d50..a845a17 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -1,5 +1,6 @@ const assert = require('assert'); const { UAParser } = require('../../src/main/ua-parser'); +const UACHTests = require('../data/ua-ch/headers'); describe('Map UA-CH headers', () => { @@ -221,232 +222,11 @@ describe('Map UA-CH headers', () => { }); describe('UA-CH Headers tests', () => { - [ - { - headers : { - 'sec-ch-ua': '"Avast Secure Browser";v="131", "Chromium";v="131", "Not_A Brand";v="24"' - }, - expect: { - browser : { - name : 'Avast Secure Browser', - version : '131', - major : '131', - type : undefined - } - } - }, - { - headers : { - 'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Brave";v="132"' - }, - expect: { - browser : { - name : 'Brave', - version : '132', - major : '132', - type : undefined - } - } - }, - { - headers : { - 'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"' - }, - expect: { - browser : { - name : 'Chrome', - version : '111', - major : '111', - type : undefined - } - } - }, - { - headers : { - 'sec-ch-ua': '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"' - }, - expect: { - 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="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"' - }, - 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 => { - const { browser } = UAParser(test.headers).withClientHints(); - assert.deepEqual(browser, test.expect.browser); + UACHTests.forEach(test => { + it(`Test for ${test.desc}`, () => { + const { browser } = UAParser(test.headers).withClientHints(); + assert.deepEqual(browser, test.expect.browser); + }); }); }); From 9bef871e41906664dd41d051e099ac540e9314af Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Tue, 2 Sep 2025 22:00:02 +0700 Subject: [PATCH 27/30] [helpers] Update isAIBot() list using Crawlers enum --- src/enums/ua-parser-enums.js | 5 +- src/helpers/ua-parser-helpers.js | 105 +++++++++++++++---------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index 7d5eb6d..a8fb97c 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -469,7 +469,7 @@ const Extension = Object.freeze({ BRAVE_BOT: 'Bravebot', BYTEDANCE_BYTESPIDER: 'Bytespider', BYTEDANCE_TIKTOKSPIDER: 'TikTokSpider', - CC_BOT: 'CCBot', + COMMON_CRAWL_CCBOT: 'CCBot', COCCOC_BOT_WEB: 'coccocbot-web', COCCOC_BOT_IMAGE: 'coccocbot-image', COHERE_TRAINING_DATA_CRAWLER: 'cohere-training-data-crawler', @@ -497,6 +497,7 @@ const Extension = Object.freeze({ GOOGLE_BOT_NEWS: 'Googlebot-News', GOOGLE_BOT_VIDEO: 'Googlebot-Video', GOOGLE_CLOUDVERTEXBOT: 'Google-CloudVertexBot', + GOOGLE_EXTENDED: 'Google-Extended', GOOGLE_INSPECTIONTOOL: 'Google-InspectionTool', GOOGLE_OTHER: 'GoogleOther', GOOGLE_OTHER_IMAGE: 'GoogleOther-Image', @@ -529,7 +530,7 @@ const Extension = Object.freeze({ ONCRAWL: 'OnCrawl', ONESPOT_SCRAPERBOT: 'Onespot-ScraperBot', OPENAI_GPTBOT: 'GPTBot', - OPENAI_SEARCH: 'OAI-SearchBot', + OPENAI_SEARCH_BOT: 'OAI-SearchBot', PERPLEXITY_BOT: 'PerplexityBot', QIHOO_360_SPIDER: '360Spider', QWANT_BOT: 'Qwantbot', diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index e8af693..d6825e0 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -8,7 +8,7 @@ /*jshint esversion: 6 */ const { UAParser } = require('../main/ua-parser'); -const { CPUArch, OSName, EngineName } = require('../enums/ua-parser-enums'); +const { CPUArch, OSName, EngineName, Extension } = require('../enums/ua-parser-enums'); const { Bots } = require('../extensions/ua-parser-extensions'); const { isFromEU } = require('detect-europe-js'); const { isFrozenUA } = require('ua-is-frozen'); @@ -41,125 +41,124 @@ const isAppleSilicon = (resultOrUA) => { return false; } +const Crawler = Extension.BrowserName.Crawlers; const isAIBot = (resultOrUA) => [ // AI2 - 'ai2bot', + Crawler.AI2_BOT, // Amazon - 'amazonbot', + Crawler.AMAZON_BOT, // Anthropic - 'anthropic-ai', - 'claude-web', - 'claude-searchbot', - 'claudebot', + Crawler.ANTHROPIC_AI, + Crawler.ANTHROPIC_CLAUDE_BOT, + Crawler.ANTHROPIC_CLAUDE_SEARCHBOT, + Crawler.ANTHROPIC_CLAUDE_WEB, // Apple - 'applebot', - 'applebot-extended', + Crawler.APPLE_BOT, + Crawler.APPLE_BOT_EXTENDED, // Brave - 'bravebot', + Crawler.BRAVE_BOT, // ByteDance - 'bytespider', - 'tiktokspider', + Crawler.BYTEDANCE_BYTESPIDER, + Crawler.BYTEDANCE_TIKTOKSPIDER, // Cohere - 'cohere-training-data-crawler', + Crawler.COHERE_TRAINING_DATA_CRAWLER, // Common Crawl - 'ccbot', + Crawler.COMMON_CRAWL_CCBOT, // Coveo - 'coveobot', + Crawler.COVEO_BOT, // DataForSeo - 'dataforseobot', + Crawler.DATAFORSEO_BOT, // DeepSeek - 'deepseekbot', + Crawler.DEEPSEEK_BOT, // Diffbot - 'diffbot', + Crawler.DIFFBOT, // Google - 'googleother', - 'googleother-image', - 'googleother-video', - 'google-cloudvertexbot', - 'google-extended', + Crawler.GOOGLE_EXTENDED, + Crawler.GOOGLE_OTHER, + Crawler.GOOGLE_OTHER_IMAGE, + Crawler.GOOGLE_OTHER_VIDEO, + Crawler.GOOGLE_CLOUDVERTEXBOT, // Hive AI - 'imagesiftbot', + Crawler.HIVE_IMAGESIFTBOT, // Huawei - 'petalbot', - 'pangubot', + Crawler.HUAWEI_PETALBOT, + Crawler.HUAWEI_PANGUBOT, // Hugging Face - 'huggingface-bot', + Crawler.HUGGINGFACE_BOT, // Kangaroo - 'kangaroo bot', + Crawler.KANGAROO_BOT, // Mendable.ai - 'firecrawlagent', + Crawler.FIRECRAWL_AGENT, // Meta - 'facebookbot', - 'meta-externalagent', + Crawler.META_FACEBOOKBOT, + Crawler.META_EXTERNALAGENT, // OpenAI - 'gptbot', - 'oai-searchbot', + Crawler.OPENAI_GPTBOT, + Crawler.OPENAI_SEARCH_BOT, // Perplexity - 'perplexitybot', + Crawler.PERPLEXITY_BOT, // Replicate - 'replicate-bot', + Crawler.REPLICATE_BOT, // Runpod - 'runpod-bot', + Crawler.RUNPOD_BOT, // SB Intuitions - 'sbintuitionsbot', + Crawler.SB_INTUITIONS_BOT, // Semrush - 'semrushbot-ocob', + Crawler.SEMRUSH_BOT_CONTENTSHAKE, // Timpi - 'timpibot', + Crawler.TIMPI_BOT, // Together AI - 'together-bot', + Crawler.TOGETHER_BOT, // Velen.io - 'velenpublicwebcrawler', + Crawler.HUNTER_VELENPUBLICWEBCRAWLER, // Vercel - 'v0bot', + Crawler.VERCEL_V0BOT, // Webz.io - 'omgili', - 'omgilibot', - 'webzio-extended', + Crawler.WEBZIO_OMGILI, + Crawler.WEBZIO_OMGILI_BOT, + Crawler.WEBZIO_EXTENDED, // X - 'xai-bot', + Crawler.XAI_BOT, // You.com - 'youbot', + Crawler.YOU_BOT, // Zhipu AI - 'chatglm-spider', - - // Zyte - 'scrapy' - - ].includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + Crawler.ZHIPU_CHATGLM_SPIDER + ] + .map((s) => s.toLowerCase()) + .includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); const isBot = (resultOrUA) => [ 'cli', From 31bf36c36d3fa1af37c71b6132a167b4604c8606 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 4 Sep 2025 20:56:18 +0700 Subject: [PATCH 28/30] [enums] enum names should be singular --- src/enums/ua-parser-enums.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index a8fb97c..dad31da 100644 --- a/src/enums/ua-parser-enums.js +++ b/src/enums/ua-parser-enums.js @@ -281,7 +281,7 @@ const DeviceVendor = Object.freeze({ SPRINT: 'Sprint', TCL: 'TCL', TECHNISAT: 'TechniSAT', - TECNO: 'Tecno', + TECNO: 'TECNO', TESLA: 'Tesla', ULEFONE: 'Ulefone', VIVO: 'Vivo', @@ -433,14 +433,14 @@ const OS = OSName; const Extension = Object.freeze({ BrowserName: { - CLIs: { + CLI: { CURL: 'curl', ELINKS: 'ELinks', HTTPIE: 'HTTPie', LYNX: 'Lynx', - WGET: 'wget' + WGET: 'Wget' }, - Crawlers: { + Crawler: { AHREFS_BOT: 'AhrefsBot', AI2_BOT: 'AI2Bot', AIHIT_BOT: 'aiHitBot', @@ -595,7 +595,7 @@ const Extension = Object.freeze({ ZHIPU_CHATGLM_SPIDER: 'ChatGLM-Spider', ZUM_BOT: 'ZumBot' }, - Emails: { + Email: { AIRMAIL: 'Airmail', APPLE_MAIL: 'Mail', BLUEMAIL: 'BlueMail', @@ -618,7 +618,7 @@ const Extension = Object.freeze({ ZIMBRA: 'Zimbra', ZOHO_MAIL: 'ZohoMail-Desktop' }, - Fetchers: { + Fetcher: { AHREFS_SITEAUDIT: 'AhrefsSiteAudit', ANTHROPIC_CLAUDE_USER: 'Claude-User', ASANA: 'Asana', @@ -671,7 +671,7 @@ const Extension = Object.freeze({ WHATSAPP: 'WhatsApp', ZOOMINFO_BOT: 'Zoombot' }, - InApps: { + InApp: { DISCORD: 'Discord', EVERNOTE: 'Evernote', FIGMA: 'Figma', @@ -687,7 +687,7 @@ const Extension = Object.freeze({ VSCODE: 'VS Code', YAHOO_JAPAN: 'Yahoo! Japan' }, - Libraries: { + Library: { ADOBE_AIR: 'AdobeAIR', AIOHTTP: 'aiohttp', APACHE_HTTPCLIENT: 'Apache-HttpClient', @@ -715,7 +715,7 @@ const Extension = Object.freeze({ } }, DeviceVendor: { - Vehicles: { + Vehicle: { BMW: 'BMW', BYD: 'BYD', JEEP: 'Jeep', From a4342b01d415ec0d09f8debf8480490c5949529c Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Thu, 4 Sep 2025 20:59:48 +0700 Subject: [PATCH 29/30] [test] Utilize enum in test cases --- src/helpers/ua-parser-helpers.js | 16 +-- test/unit/extensions.js | 28 +++--- test/unit/helpers.js | 9 +- test/unit/ua-ch.js | 165 ++++++++++++++++--------------- 4 files changed, 111 insertions(+), 107 deletions(-) diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js index d6825e0..54ce86f 100644 --- a/src/helpers/ua-parser-helpers.js +++ b/src/helpers/ua-parser-helpers.js @@ -8,11 +8,12 @@ /*jshint esversion: 6 */ const { UAParser } = require('../main/ua-parser'); -const { CPUArch, OSName, EngineName, Extension } = require('../enums/ua-parser-enums'); -const { Bots } = require('../extensions/ua-parser-extensions'); +const { CPUArch, OSName, EngineName, Extension, BrowserType } = require('../enums/ua-parser-enums'); +const { Bots, Crawlers } = require('../extensions/ua-parser-extensions'); const { isFromEU } = require('detect-europe-js'); const { isFrozenUA } = require('ua-is-frozen'); const { isStandalonePWA } = require('is-standalone-pwa'); +const { Crawler } = Extension.BrowserName; const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value; @@ -41,7 +42,6 @@ const isAppleSilicon = (resultOrUA) => { return false; } -const Crawler = Extension.BrowserName.Crawlers; const isAIBot = (resultOrUA) => [ // AI2 @@ -158,13 +158,13 @@ const isAIBot = (resultOrUA) => [ Crawler.ZHIPU_CHATGLM_SPIDER ] .map((s) => s.toLowerCase()) - .includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + .includes(String(toResult(resultOrUA, Crawlers).browser.name).toLowerCase()); const isBot = (resultOrUA) => [ - 'cli', - 'crawler', - 'fetcher', - 'library' + BrowserType.CLI, + BrowserType.CRAWLER, + BrowserType.FETCHER, + BrowserType.LIBRARY ].includes(toResult(resultOrUA, Bots).browser.type); const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(EngineName.BLINK); diff --git a/test/unit/extensions.js b/test/unit/extensions.js index 9f1fa96..c3acfa2 100644 --- a/test/unit/extensions.js +++ b/test/unit/extensions.js @@ -5,6 +5,8 @@ const traverse = require('@babel/traverse').default; const safe = require('safe-regex'); const { UAParser } = require('../../src/main/ua-parser'); const { Bots, CLIs, Crawlers, Emails, Fetchers, InApps, Libraries, Vehicles } = require('../../src/extensions/ua-parser-extensions'); +const { BrowserType, OSName, Extension } = require('../../src/enums/ua-parser-enums'); +const { CLI, Crawler, Email, Fetcher, Library } = Extension.BrowserName; describe('Extensions', () => { [ @@ -42,29 +44,29 @@ 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'); + assert.equal(UAParser(scrapy, Bots).browser.name, Library.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"}); + assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: Email.MICROSOFT_OUTLOOK, version: "16.0.9126", major: "16", type: BrowserType.EMAIL}); + assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: Email.THUNDERBIRD, version: "78.13.0", major: "78", type: BrowserType.EMAIL}); 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"}); + assert.deepEqual(libraryParser.setUA(axios).getBrowser(), {name: Library.AXIOS, version: "1.3.5", major: "1", type: BrowserType.LIBRARY}); + assert.deepEqual(libraryParser.setUA(jsdom).getBrowser(), {name: Library.JSDOM, version: "20.0.3", major: "20", type: BrowserType.LIBRARY}); + assert.deepEqual(libraryParser.setUA(scrapy).getBrowser(), {name: Library.SCRAPY, version: "1.5.0", major: "1", type: BrowserType.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', + name: Fetcher.BLUESKY, version: '1.1', major: '1', - type: 'fetcher' + type: BrowserType.FETCHER }); const whatsapp = "WhatsApp/2.0 A"; assert.deepEqual(new UAParser(whatsapp, Fetchers).getOS(), { - name : 'Android', + name : OSName.ANDROID, version : undefined }); }); @@ -77,14 +79,14 @@ describe('Merge', () => { // 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"}); + assert.deepEqual(crawlersAndCLIsParser.setUA(wget).getBrowser(), {name: CLI.WGET, version: "1.21.1", major: "1", type: BrowserType.CLI}); + assert.deepEqual(crawlersAndCLIsParser.setUA(facebookBot).getBrowser(), {name: Crawler.META_FACEBOOKBOT, version: "1.0", major: "1", type: BrowserType.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"}); + assert.deepEqual(crawlersAndCLIsParser2.setUA(wget).getBrowser(), {name: CLI.WGET, version: "1.21.1", major: "1", type: BrowserType.CLI}); + assert.deepEqual(crawlersAndCLIsParser3.getBrowser(), {name: Crawler.META_FACEBOOKBOT, version: "1.0", major: "1", type: BrowserType.CRAWLER}); }); }); diff --git a/test/unit/helpers.js b/test/unit/helpers.js index 127133b..3fa7925 100644 --- a/test/unit/helpers.js +++ b/test/unit/helpers.js @@ -2,6 +2,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 { DeviceVendor } = require('../../src/enums/ua-parser-enums'); describe('getDeviceVendor', () => { it('Can guess the device vendor from a model name', () => { @@ -11,10 +12,10 @@ describe('getDeviceVendor', () => { const modelNexus = 'Nexus 6P'; const modelAquos = 'AQUOS-TVX19B'; - assert.equal(getDeviceVendor(modelSM), 'Samsung'); - assert.equal(getDeviceVendor(modelRedmi), 'Xiaomi'); - assert.equal(getDeviceVendor(modelNexus), 'Huawei'); - assert.equal(getDeviceVendor(modelAquos), 'Sharp'); + assert.equal(getDeviceVendor(modelSM), DeviceVendor.SAMSUNG); + assert.equal(getDeviceVendor(modelRedmi), DeviceVendor.XIAOMI); + assert.equal(getDeviceVendor(modelNexus), DeviceVendor.HUAWEI); + assert.equal(getDeviceVendor(modelAquos), DeviceVendor.SHARP); }); }); diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js index a845a17..fd49f72 100644 --- a/test/unit/ua-ch.js +++ b/test/unit/ua-ch.js @@ -1,5 +1,6 @@ const assert = require('assert'); const { UAParser } = require('../../src/main/ua-parser'); +const { BrowserName, CPUArch, DeviceType, DeviceVendor, EngineName, OSName } = require('../../src/enums/ua-parser-enums'); const UACHTests = require('../data/ua-ch/headers'); describe('Map UA-CH headers', () => { @@ -26,27 +27,27 @@ describe('Map UA-CH headers', () => { 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.name, BrowserName.CHROME); assert.strictEqual(uap.browser.version, "93.0.1.2"); assert.strictEqual(uap.browser.major, "93"); - assert.strictEqual(browser.name, "Chrome"); + assert.strictEqual(browser.name, BrowserName.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.cpu.architecture, CPUArch.ARM_64); + assert.strictEqual(cpu.architecture, CPUArch.ARM_64); + assert.strictEqual(uap.device.type, DeviceType.MOBILE); assert.strictEqual(uap.device.model, "Pixel 99"); - assert.strictEqual(uap.device.vendor, "Google"); - assert.strictEqual(device.type, "mobile"); + assert.strictEqual(uap.device.vendor, DeviceVendor.GOOGLE); + assert.strictEqual(device.type, DeviceType.MOBILE); assert.strictEqual(device.model, "Pixel 99"); - assert.strictEqual(device.vendor, "Google"); - assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(device.vendor, DeviceVendor.GOOGLE); + assert.strictEqual(uap.engine.name, EngineName.BLINK); assert.strictEqual(uap.engine.version, '93.0.1.2'); - assert.strictEqual(engine.name, 'Blink'); + assert.strictEqual(engine.name, EngineName.BLINK); assert.strictEqual(engine.version, '93.0.1.2'); - assert.strictEqual(uap.os.name, "Windows"); + assert.strictEqual(uap.os.name, OSName.WINDOWS); assert.strictEqual(uap.os.version, "11"); - assert.strictEqual(os.name, "Windows"); + assert.strictEqual(os.name, OSName.WINDOWS); assert.strictEqual(os.version, "11"); }); @@ -59,16 +60,16 @@ describe('Map UA-CH headers', () => { engine = new UAParser(headers).getEngine(); os = new UAParser(headers).getOS(); - assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.name, BrowserName.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.cpu.architecture, CPUArch.X86_64); 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.name, EngineName.BLINK); assert.strictEqual(uap.engine.version, '110.0.0.0'); - assert.strictEqual(uap.os.name, "Linux"); + assert.strictEqual(uap.os.name, OSName.LINUX); assert.strictEqual(uap.os.version, undefined); }); @@ -81,16 +82,16 @@ describe('Map UA-CH headers', () => { uap = UAParser(headers2).withClientHints(); - assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.name, BrowserName.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.cpu.architecture, CPUArch.X86_64); + assert.strictEqual(uap.device.type, DeviceType.MOBILE); assert.strictEqual(uap.device.model, undefined); assert.strictEqual(uap.device.vendor, undefined); - assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.name, EngineName.BLINK); assert.strictEqual(uap.engine.version, '110.0.0.0'); - assert.strictEqual(uap.os.name, "Linux"); + assert.strictEqual(uap.os.name, OSName.LINUX); assert.strictEqual(uap.os.version, undefined); }); @@ -117,10 +118,10 @@ describe('Map UA-CH headers', () => { } */ - 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); + assert.strictEqual(ua.os.is(OSName.MACOS), true); + assert.strictEqual(ua.cpu.is(CPUArch.ARM), true); + assert.strictEqual(ua.device.is(DeviceType.MOBILE), false); + assert.strictEqual(ua.device.is(DeviceType.TABLET), false); }); }); @@ -139,11 +140,11 @@ describe('Map UA-CH headers', () => { }; UAParser(FFVR).withClientHints().then(ua => { - assert.strictEqual(ua.device.type, 'xr'); + assert.strictEqual(ua.device.type, DeviceType.XR); }); UAParser(FFEInk).withClientHints().then(ua => { - assert.strictEqual(ua.device.type, 'tablet'); + assert.strictEqual(ua.device.type, DeviceType.TABLET); }); @@ -169,7 +170,7 @@ describe('Map UA-CH headers', () => { uap = UAParser(headers2).withClientHints(); - assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.name, BrowserName.CHROME); assert.strictEqual(uap.browser.version, undefined); assert.strictEqual(uap.browser.major, undefined); }); @@ -196,27 +197,27 @@ describe('Map UA-CH headers', () => { }; uap = UAParser(headers3a).withClientHints(); - assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.name, BrowserName.CHROME); assert.strictEqual(uap.browser.version, "120.0.6099.132"); uap = UAParser(headers3b).withClientHints(); - assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.name, BrowserName.CHROME); assert.strictEqual(uap.browser.version, "120.0.6099.132"); uap = UAParser(headers3c).withClientHints(); - assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.name, BrowserName.CHROME); assert.strictEqual(uap.browser.version, "120.0.6099.132"); uap = UAParser(headers3d).withClientHints(); - assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.name, BrowserName.EDGE); assert.strictEqual(uap.browser.version, "120.0.6099.133"); uap = UAParser(headers3e).withClientHints(); - assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.name, BrowserName.EDGE); assert.strictEqual(uap.browser.version, "120.0.6099.133"); uap = UAParser(headers3f).withClientHints(); - assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.name, BrowserName.EDGE); assert.strictEqual(uap.browser.version, "120.0.6099.133"); }); }); @@ -235,169 +236,169 @@ describe('Identify vendor & type of device from given model name', () => { { model: '220733SG', expect: { - vendor : 'Xiaomi', - type : 'mobile' + vendor : DeviceVendor.XIAOMI, + type : DeviceType.MOBILE } }, { model: '5087Z', expect: { - vendor : 'TCL', - type : 'mobile' + vendor : DeviceVendor.TCL, + type : DeviceType.MOBILE } }, { model: '9137W', expect: { - vendor : 'TCL', - type : 'tablet' + vendor : DeviceVendor.TCL, + type : DeviceType.TABLET } }, { model: 'BE2015', expect: { - vendor : 'OnePlus', - type : 'mobile' + vendor : DeviceVendor.ONEPLUS, + type : DeviceType.MOBILE } }, { model: 'CPH2389', expect: { - vendor : 'OnePlus', - type : 'mobile' + vendor : DeviceVendor.ONEPLUS, + type : DeviceType.MOBILE } }, { model: 'Infinix X669C', expect: { - vendor : 'Infinix', - type : 'mobile' + vendor : DeviceVendor.INFINIX, + type : DeviceType.MOBILE } }, { model: 'itel L6502', expect: { - vendor : 'itel', - type : 'mobile' + vendor : DeviceVendor.ITEL, + type : DeviceType.MOBILE } }, { model: 'Lenovo TB-X606F', expect: { - vendor : 'Lenovo', - type : 'tablet' + vendor : DeviceVendor.LENOVO, + type : DeviceType.TABLET } }, { model: 'LM-Q720', expect: { - vendor : 'LG', - type : 'mobile' + vendor : DeviceVendor.LG, + type : DeviceType.MOBILE } }, { model: 'M2003J15SC', expect: { - vendor : 'Xiaomi', - type : 'mobile' + vendor : DeviceVendor.XIAOMI, + type : DeviceType.MOBILE } }, { model: 'MAR-LX1A', expect: { - vendor : 'Huawei', - type : 'mobile' + vendor : DeviceVendor.HUAWEI, + type : DeviceType.MOBILE } }, { model: 'moto g(20)', expect: { - vendor : 'Motorola', - type : 'mobile' + vendor : DeviceVendor.MOTOROLA, + type : DeviceType.MOBILE } }, { model: 'Nokia C210', expect: { - vendor : 'Nokia', - type : 'mobile' + vendor : DeviceVendor.NOKIA, + type : DeviceType.MOBILE } }, { model: 'Pixel 8', expect: { - vendor : 'Google', - type : 'mobile' + vendor : DeviceVendor.GOOGLE, + type : DeviceType.MOBILE } }, { model: 'Redmi Note 9S', expect: { - vendor : 'Xiaomi', - type : 'mobile' + vendor : DeviceVendor.XIAOMI, + type : DeviceType.MOBILE } }, { model: 'RMX3830', expect: { - vendor : 'Realme', - type : 'mobile' + vendor : DeviceVendor.REALME, + type : DeviceType.MOBILE } }, { model: 'SM-S536DL', expect: { - vendor : 'Samsung', - type : 'mobile' + vendor : DeviceVendor.SAMSUNG, + type : DeviceType.MOBILE } }, { model: 'SM-S546VL', expect: { - vendor : 'Samsung', - type : 'mobile' + vendor : DeviceVendor.SAMSUNG, + type : DeviceType.MOBILE } }, { model: 'SM-T875', expect: { - vendor : 'Samsung', - type : 'tablet' + vendor : DeviceVendor.SAMSUNG, + type : DeviceType.TABLET } }, { model: 'STK-L21', expect: { - vendor : 'Huawei', - type : 'mobile' + vendor : DeviceVendor.HUAWEI, + type : DeviceType.MOBILE } }, { model: 'T430W', expect: { - vendor : 'TCL', - type : 'mobile' + vendor : DeviceVendor.TCL, + type : DeviceType.MOBILE } }, { model: 'TECNO KI5k', expect: { - vendor : 'TECNO', - type : 'mobile' + vendor : DeviceVendor.TECNO, + type : DeviceType.MOBILE } }, { model: 'vivo 1820', expect: { - vendor : 'Vivo', - type : 'mobile' + vendor : DeviceVendor.VIVO, + type : DeviceType.MOBILE } }, { model: 'Xbox', expect: { - vendor : 'Microsoft', - type : 'console' + vendor : DeviceVendor.MICROSOFT, + type : DeviceType.CONSOLE } } ] From 3ea5721e86bc6d5860c9a53cc9d82b16791df54b Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Fri, 5 Sep 2025 21:43:09 +0700 Subject: [PATCH 30/30] Bump version `2.0.5` --- CHANGELOG.md | 39 +++ README.md | 71 +++-- 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 | 46 +-- package.json | 2 +- src/enums/ua-parser-enums.d.ts | 85 ++++-- src/enums/ua-parser-enums.js | 5 +- src/enums/ua-parser-enums.mjs | 362 ++++++++++++++++++++++- src/extensions/ua-parser-extensions.d.ts | 2 +- src/extensions/ua-parser-extensions.js | 6 +- src/extensions/ua-parser-extensions.mjs | 53 +++- src/helpers/ua-parser-helpers.d.ts | 2 +- src/helpers/ua-parser-helpers.js | 2 +- src/helpers/ua-parser-helpers.mjs | 132 ++++++--- src/main/ua-parser.d.ts | 2 +- src/main/ua-parser.js | 4 +- src/main/ua-parser.mjs | 54 ++-- test/data/ua/extension/fetcher.json | 10 - 21 files changed, 678 insertions(+), 215 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1f9ac6..8ea4bb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,35 @@ --- +## Version 2.0.5 + +- Add new browser: Zalo +- Add new CPU arch: alpha +- Add new device vendor: Philips +- Improve device detection: Pico +- Fix parsing error on pages with modified Array prototypes +- Improve type declarations: + - Replace `node-fetch` dependency with `undici` + - Replace hardcoded string values with enum from `enum` submodule +- `enums` submodule: + - Add `Extension` enum for `extensions` submodule + - Type declaration file now automatically generated using build script + - Naming adjustments: + - `Browser` => `BrowserName` + - `CPU` => `CPUArch` + - `Device` => `DeviceType` + - `Vendor` => `DeviceVendor` + - `Engine` => `EngineName` + - `OS` => `OSName` +- `extensions` submodule: + - Add new crawlers: + APIs-Google, Algolia Crawler, Algolia Crawler Renderscript, Baidu-ADS, BLEXBot, botify, Bravebot, Claude-Web, cohere-training-data-crawler, contxbot, Cotoyogi, Coveobot, CriteoBot, DeepSeekBot, DuckDuckGo-Favicons-Bot, Elastic, FirecrawlAgent, Freespoke, Google-CloudVertexBot, HuggingFace-Bot, Kagibot, Kangaroo Bot, marginalia, msnbot, OnCrawl, Replicate-Bot, RunPod-Bot, SBIntuitionsBot, SeekportBot, Siteimprove, Sogou Pic Spider, TikTokSpider, TwinAgent, v0bot, webzio, Webzio-Extended, xAI-Bot, YandexAccessibilityBot, YandexAdditionalBot, YandexAdNet, YandexBot MirrorDetector, YandexBlogs, YandexComBot, YandexFavicons, YandexImageResizer, YandexImages, YandexMarket, YandexMetrika, YandexMedia, YandexMobileBot, YandexMobileScreenShotBot, YandexNews, YandexOntoDB, YandexOntoDBAPI, YandexPartner, YandexRCA, YandexRenderResourcesBot, YandexScreenshotBot, YandexSpravBot, YandexTracker, YandexVertis, YandexVerticals, YandexVideo, YandexVideoParser, YandexWebmaster, YepBot, ZumBot + - Add new fetchers: + Asana, bitlybot, Blueno, BufferLinkPreviewBot, Chrome-Lighthouse, Gemini-Deep-Research, HubSpot Page Fetcher, kakaotalk-scrap, vercel-favicon-bot, vercel-screenshot-bot, vercelflags, verceltracing, YaDirectFetcher, YandexCalendar, YandexDirect, YandexDirectDyn, YandexForDomain, YandexPagechecker, YandexSearchShop, YandexSitelinks, YandexUserproxy +- `helpers` submodule: + - Add some crawler to `isAIBot()`: + Bravebot, cohere-training-data-crawler, FirecrawlAgent, HuggingFace-Bot, Kangaroo Bot, PanguBot, Replicate-Bot, RunPod-Bot, TikTokSpider, Together-Bot, v0bot, xAI-Bot + ## Version 2.0.4 - Add new browser: Edge WebView, Edge WebView2 @@ -240,6 +269,16 @@ --- +## Version 0.7.41 / 1.0.41 +- Add new browser: Daum, Ladybird +- Add new device vendor: HMD +- Add new engine: LibWeb +- Add new os: Windows IoT, Ubuntu Touch +- Improve cpu detection: ARM, x86 +- Improve device vendor detection: Apple, Archos, Generic, Google, Honor, Huawei, Infinix, Nvidia, Lenovo, Nokia, OnePlus, Xiaomi +- Improve device type detection: smarttv, wearables +- Improve os detection: Linux, Symbian + ## Version 0.7.40 / 1.0.40 - Add new browser: 115, LibreWolf, Slimboat, Slimjet - Add new device: Advan, Cat, Energizer, IMO, Micromax, Smartfren diff --git a/README.md b/README.md index 0ea5465..79c1f73 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,12 @@ +Discord invite

# UAParser.js -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). +The most comprehensive, compact, and up-to-date JavaScript library to detect user's browser, OS, CPU, and device type/model. Also detect bots, apps, and more. Runs seamlessly in the browser (client-side) or Node.js (server-side). # Demo @@ -33,7 +32,7 @@ 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. -# License Options +# Package & Pricing @@ -46,8 +45,8 @@ see what's new & breaking. - - + + @@ -148,14 +147,6 @@ see what's new & breaking. - - - - - - - - @@ -165,7 +156,23 @@ see what's new & breaking. - + + + + + + + + + + + + + + + + + @@ -181,11 +188,27 @@ see what's new & breaking. - + + + - + + + + + + + + + + + + + + + @@ -206,8 +229,8 @@ see what's new & breaking. - - + + @@ -236,10 +259,7 @@ Made with [contributors-img](https://contrib.rocks). ## Backers & Sponsors - - - -You can support the open-source editions of UAParser.js through one of the following options: +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) @@ -248,4 +268,7 @@ You can support the open-source editions of UAParser.js through one of the follo [![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 +)](https://store.faisalman.com/buy/3d71f2f3-cf4d-473c-892a-9d4497c890be) + + + \ No newline at end of file diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 22782df..98f9ad6 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.4 +/* UAParser.js v2.0.5 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){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 +(function(window,undefined){"use strict";var LIBVERSION="2.0.5",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){if(typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[String(k).toLowerCase()]=v});headers=kv}else{var normalized={};for(var header in headers){if(headers.hasOwnProperty(header)){normalized[String(header).toLowerCase()]=headers[header]}}headers=normalized}}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 c8afed4..ce5f6d4 100644 --- a/dist/ua-parser.min.mjs +++ b/dist/ua-parser.min.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.4 +/* UAParser.js v2.0.5 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){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 +var LIBVERSION="2.0.5",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){if(typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[String(k).toLowerCase()]=v});headers=kv}else{var normalized={};for(var header in headers){if(headers.hasOwnProperty(header)){normalized[String(header).toLowerCase()]=headers[header]}}headers=normalized}}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 fec42a7..cd05ecb 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.4 +/* UAParser.js v2.0.5 Copyright © 2012-2025 Faisal Salman AGPLv3 License */ -((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 +((i,c)=>{function V(i){for(var e={},t=0;t{var t,o={},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(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",(b=function(i){return i==g?function(){return new Gi(i,s,w,n).set("ua",s).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,s,w[i],n).parseUA().get()}})(u)],["getCPU",b(h)],["getDevice",b(p)],["getEngine",b(m)],["getOS",b(f)],["getResult",b(g)],["getUA",function(){return s}],["setUA",function(i){return Oi(i)&&(s=i.length>I?Mi(i,I):i),this}]]).setUA(s),this):new P(i,e,t).getResult()}P.VERSION="2.0.5",P.BROWSER=V([v,y,G,k]),P.CPU=V([C]),P.DEVICE=V([S,x,k,W,_,e,r,t,F]),P.ENGINE=P.OS=V([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=P:exports).UAParser=P:typeof define===L&&define.amd?define(function(){return P}):_i&&(i.UAParser=P);var Wi,Ni=_i&&(i.jQuery||i.Zepto);Ni&&!Ni.ua&&(Wi=new P,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 8ab1e0e..d8b552f 100644 --- a/dist/ua-parser.pack.mjs +++ b/dist/ua-parser.pack.mjs @@ -1,4 +1,4 @@ -/* UAParser.js v2.0.4 +/* UAParser.js v2.0.5 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(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 +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",(d=function(i){return i==f?function(){return new Li(i,s,w,n).set("ua",s).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,s,w[i],n).parseUA().get()}})(c)],["getCPU",d(h)],["getDevice",d(u)],["getEngine",d(p)],["getOS",d(m)],["getResult",d(f)],["getUA",function(){return s}],["setUA",function(i){return H(i)&&(s=i.length>P?Mi(i,P):i),this}]]).setUA(s),this):new V(i,e,o).getResult()}V.VERSION="2.0.5",V.BROWSER=I([g,x,G,v]),V.CPU=I([y]),V.DEVICE=I([C,k,v,W,S,i,r,e,F]),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 1265e3d..b9334f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ua-parser-js", - "version": "2.0.4", + "version": "2.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ua-parser-js", - "version": "2.0.4", + "version": "2.0.5", "funding": [ { "type": "opencollective", @@ -1889,26 +1889,6 @@ "node": "^10 || ^12 || >=13.7" } }, - "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/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -2640,12 +2620,6 @@ "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", @@ -2738,22 +2712,6 @@ "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/package.json b/package.json index e1477d8..580bbcc 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "UAParser.js", "name": "ua-parser-js", - "version": "2.0.4", + "version": "2.0.5", "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 b748ea9..c19cd80 100644 --- a/src/enums/ua-parser-enums.d.ts +++ b/src/enums/ua-parser-enums.d.ts @@ -3,7 +3,7 @@ // Source: /src/enums/ua-parser-enums.js /////////////////////////////////////////////// -/* Enums for UAParser.js v2.0.4 +/* Enums for UAParser.js v2.0.5 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -285,7 +285,7 @@ export const DeviceVendor: Readonly<{ SPRINT: 'Sprint', TCL: 'TCL', TECHNISAT: 'TechniSAT', - TECNO: 'Tecno', + TECNO: 'TECNO', TESLA: 'Tesla', ULEFONE: 'Ulefone', VIVO: 'Vivo', @@ -437,15 +437,14 @@ export const OS: typeof OSName; export const Extension: Readonly<{ BrowserName: { - CLIs: { + CLI: { CURL: 'curl', ELINKS: 'ELinks', HTTPIE: 'HTTPie', LYNX: 'Lynx', - WGET: 'wget' + WGET: 'Wget' }, - Crawlers: { - '360_SPIDER': '360Spider', + Crawler: { AHREFS_BOT: 'AhrefsBot', AI2_BOT: 'AI2Bot', AIHIT_BOT: 'aiHitBot', @@ -472,9 +471,9 @@ export const Extension: Readonly<{ BLEX_BOT: 'BLEXBot', BOTIFY: 'botify', BRAVE_BOT: 'Bravebot', - BYTEDANCE_SPIDER: 'Bytespider', - CC_BOT: 'CCBot', - CHATGLM_SPIDER: 'ChatGLM-Spider', + BYTEDANCE_BYTESPIDER: 'Bytespider', + BYTEDANCE_TIKTOKSPIDER: 'TikTokSpider', + COMMON_CRAWL_CCBOT: 'CCBot', COCCOC_BOT_WEB: 'coccocbot-web', COCCOC_BOT_IMAGE: 'coccocbot-image', COHERE_TRAINING_DATA_CRAWLER: 'cohere-training-data-crawler', @@ -496,10 +495,13 @@ export const Extension: Readonly<{ GOOGLE_ADSBOT: 'AdsBot-Google', GOOGLE_ADSBOT_MOBILE: 'Adsbot-Google-Mobile', GOOGLE_ADSENSE: 'AdSense', + GOOGLE_APIS: 'APIs-Google', GOOGLE_BOT: 'Googlebot', GOOGLE_BOT_IMAGE: 'Googlebot-Image', GOOGLE_BOT_NEWS: 'Googlebot-News', GOOGLE_BOT_VIDEO: 'Googlebot-Video', + GOOGLE_CLOUDVERTEXBOT: 'Google-CloudVertexBot', + GOOGLE_EXTENDED: 'Google-Extended', GOOGLE_INSPECTIONTOOL: 'Google-InspectionTool', GOOGLE_OTHER: 'GoogleOther', GOOGLE_OTHER_IMAGE: 'GoogleOther-Image', @@ -529,16 +531,16 @@ export const Extension: Readonly<{ MICROSOFT_ADIDXBOT: 'adidxbot', MOJEEK_BOT: 'MojeekBot', MOZ_DOTBOT: 'DotBot', - OMGILI: 'omgili', - OMGILI_BOT: 'omgilibot', ONCRAWL: 'OnCrawl', ONESPOT_SCRAPERBOT: 'Onespot-ScraperBot', OPENAI_GPTBOT: 'GPTBot', - OPENAI_SEARCH: 'OAI-SearchBot', + OPENAI_SEARCH_BOT: 'OAI-SearchBot', PERPLEXITY_BOT: 'PerplexityBot', + QIHOO_360_SPIDER: '360Spider', QWANT_BOT: 'Qwantbot', REPLICATE_BOT: 'Replicate-Bot', RUNPOD_BOT: 'RunPod-Bot', + SB_INTUITIONS_BOT: 'SBIntuitionsBot', SEEKPORT_BOT: 'SeekportBot', SEMRUSH_BOT: 'SemrushBot', SEMRUSH_BOT_BACKLINK: 'SemrushBot-BA', @@ -553,18 +555,51 @@ export const Extension: Readonly<{ TOGETHER_BOT: 'Together-Bot', TURNITIN_BOT: 'TurnitinBot', TWIN_AGENT: 'TwinAgent', - XAI_BOT: 'xAI-Bot', VERCEL_V0BOT: 'v0bot', + WEBZIO: 'webzio', + WEBZIO_EXTENDED: 'Webzio-Extended', + WEBZIO_OMGILI: 'omgili', + WEBZIO_OMGILI_BOT: 'omgilibot', + XAI_BOT: 'xAI-Bot', YAHOO_JAPAN: 'Y!J-BRW', YAHOO_SLURP: 'Yahoo! Slurp', + YANDEX_ACCESSIBILITY_BOT: 'YandexAccessibilityBot', + YANDEX_ADDITIONAL_BOT: 'YandexAdditionalBot', + YANDEX_ADNET: 'YandexAdNet', + YANDEX_BLOGS: 'YandexBlogs', YANDEX_BOT: 'YandexBot', + YANDEX_BOT_MIRRORDETECTOR: 'YandexBot MirrorDetector', + YANDEX_COMBOT: 'YandexComBot', + YANDEX_FAVICONS: 'YandexFavicons', + YANDEX_IMAGE_RESIZER: 'YandexImageResizer', + YANDEX_IMAGES: 'YandexImages', + YANDEX_MARKET: 'YandexMarket', + YANDEX_MEDIA: 'YandexMedia', + YANDEX_METRIKA: 'YandexMetrika', + YANDEX_MOBILE_BOT: 'YandexMobileBot', + YANDEX_MOBILE_SCREENSHOT_BOT: 'YandexMobileScreenShotBot', + YANDEX_NEWS: 'YandexNews', + YANDEX_ONTODB: 'YandexOntoDB', + YANDEX_ONTODB_API: 'YandexOntoDBAPI', + YANDEX_PARTNER: 'YandexPartner', + YANDEX_RCA: 'YandexRCA', + YANDEX_RENDERRESOURCES_BOT: 'YandexRenderResourcesBot', + YANDEX_SCREENSHOT_BOT: 'YandexScreenshotBot', + YANDEX_SPRAV_BOT: 'YandexSpravBot', + YANDEX_TRACKER: 'YandexTracker', + YANDEX_VERTICALS: 'YandexVerticals', + YANDEX_VERTIS: 'YandexVertis', + YANDEX_VIDEO: 'YandexVideo', + YANDEX_VIDEO_PARSER: 'YandexVideoParser', + YANDEX_WEBMASTER: 'YandexWebmaster', YEP_BOT: 'YepBot', YETI: 'Yeti', YISOU_SPIDER: 'YisouSpider', YOU_BOT: 'YouBot', + ZHIPU_CHATGLM_SPIDER: 'ChatGLM-Spider', ZUM_BOT: 'ZumBot' }, - Emails: { + Email: { AIRMAIL: 'Airmail', APPLE_MAIL: 'Mail', BLUEMAIL: 'BlueMail', @@ -587,7 +622,7 @@ export const Extension: Readonly<{ ZIMBRA: 'Zimbra', ZOHO_MAIL: 'ZohoMail-Desktop' }, - Fetchers: { + Fetcher: { AHREFS_SITEAUDIT: 'AhrefsSiteAudit', ANTHROPIC_CLAUDE_USER: 'Claude-User', ASANA: 'Asana', @@ -600,7 +635,7 @@ export const Extension: Readonly<{ GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', GOOGLE_GEMINI_DEEP_RESEARCH: 'Gemini-Deep-Research', - GOOGLE_IMAGE_PROXY: 'GoogleImageProxy', + GOOGLE_IMAGEPROXY: 'GoogleImageProxy', GOOGLE_PAGERENDERER: 'Google-PageRenderer', GOOGLE_READ_ALOUD: 'Google-Read-Aloud', GOOGLE_PRODUCER: 'GoogleProducer', @@ -609,6 +644,7 @@ export const Extension: Readonly<{ IFRAMELY: 'Iframely', KAKAOTALK_SCRAP: 'kakaotalk-scrap', META_EXTERNALFETCHER: 'meta-externalfetcher', + META_WHATSAPP: 'WhatsApp', MICROSOFT_BINGPREVIEW: 'BingPreview', MICROSOFT_PREVIEW: 'MicrosoftPreview', MISTRALAI_USER: 'MistralAI-User', @@ -621,17 +657,24 @@ export const Extension: Readonly<{ SNAP_URL_PREVIEW: 'Snap URL Preview', SKYPE_URIPREVIEW: 'SkypeUriPreview', TELEGRAM_BOT: 'TelegramBot', - TIKTOK_SPIDER: 'TikTokSpider', UPTIMEROBOT: 'UptimeRobot', VERCEL_FAVICON_BOT: 'vercel-favicon-bot', VERCEL_SCREENSHOT_BOT: 'vercel-screenshot-bot', VERCEL_BOT: 'Vercelbot', VERCEL_FLAGS: 'vercelflags', VERCEL_TRACING: 'verceltracing', - WHATSAPP: 'WhatsApp', + YANDEX_CALENDAR: 'YandexCalendar', + YANDEX_DIRECT: 'YandexDirect', + YANDEX_DIRECTDYN: 'YandexDirectDyn', + YANDEX_DIRECTFETCHER: 'YaDirectFetcher', + YANDEX_FORDOMAIN: 'YandexForDomain', + YANDEX_PAGECHECKER: 'YandexPagechecker', + YANDEX_SEARCHSHOP: 'YandexSearchShop', + YANDEX_SITELINKS: 'YandexSitelinks', + YANDEX_USERPROXY: 'YandexUserproxy', ZOOMINFO_BOT: 'Zoombot' }, - InApps: { + InApp: { DISCORD: 'Discord', EVERNOTE: 'Evernote', FIGMA: 'Figma', @@ -647,7 +690,7 @@ export const Extension: Readonly<{ VSCODE: 'VS Code', YAHOO_JAPAN: 'Yahoo! Japan' }, - Libraries: { + Library: { ADOBE_AIR: 'AdobeAIR', AIOHTTP: 'aiohttp', APACHE_HTTPCLIENT: 'Apache-HttpClient', @@ -675,7 +718,7 @@ export const Extension: Readonly<{ } }, DeviceVendor: { - Vehicles: { + Vehicle: { BMW: 'BMW', BYD: 'BYD', JEEP: 'Jeep', diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js index dad31da..1dd8d9f 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.4 +/* Enums for UAParser.js v2.0.5 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -640,6 +640,7 @@ const Extension = Object.freeze({ IFRAMELY: 'Iframely', KAKAOTALK_SCRAP: 'kakaotalk-scrap', META_EXTERNALFETCHER: 'meta-externalfetcher', + META_WHATSAPP: 'WhatsApp', MICROSOFT_BINGPREVIEW: 'BingPreview', MICROSOFT_PREVIEW: 'MicrosoftPreview', MISTRALAI_USER: 'MistralAI-User', @@ -652,7 +653,6 @@ const Extension = Object.freeze({ SNAP_URL_PREVIEW: 'Snap URL Preview', SKYPE_URIPREVIEW: 'SkypeUriPreview', TELEGRAM_BOT: 'TelegramBot', - TIKTOK_SPIDER: 'TikTokSpider', UPTIMEROBOT: 'UptimeRobot', VERCEL_FAVICON_BOT: 'vercel-favicon-bot', VERCEL_SCREENSHOT_BOT: 'vercel-screenshot-bot', @@ -668,7 +668,6 @@ const Extension = Object.freeze({ YANDEX_SEARCHSHOP: 'YandexSearchShop', YANDEX_SITELINKS: 'YandexSitelinks', YANDEX_USERPROXY: 'YandexUserproxy', - WHATSAPP: 'WhatsApp', ZOOMINFO_BOT: 'Zoombot' }, InApp: { diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs index 089af95..9c1f3f1 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.4 +/* Enums for UAParser.js v2.0.5 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -11,7 +11,7 @@ /*jshint esversion: 6 */ -const Browser = Object.freeze({ +const BrowserName = Object.freeze({ '115': '115', '2345': '2345', '360': '360', @@ -162,10 +162,15 @@ const Browser = Object.freeze({ WEIBO: 'Weibo', WHALE: 'Whale', WOLVIC: 'Wolvic', - YANDEX: 'Yandex' + YANDEX: 'Yandex', + ZALO: 'Zalo' // TODO : test! }); +/** + * @deprecated Use `BrowserName` instead + */ +const Browser = BrowserName; const BrowserType = Object.freeze({ CRAWLER: 'crawler', @@ -177,8 +182,9 @@ const BrowserType = Object.freeze({ LIBRARY: 'library' }); -const CPU = Object.freeze({ +const CPUArch = Object.freeze({ '68K': '68k', + ALPHA: 'alpha', ARM : 'arm', ARM_64: 'arm64', ARM_HF: 'armhf', @@ -196,8 +202,12 @@ const CPU = Object.freeze({ X86: 'ia32', X86_64: 'amd64' }); +/** + * @deprecated Use `CPUArch` instead + */ +const CPU = CPUArch; -const Device = Object.freeze({ +const DeviceType = Object.freeze({ CONSOLE: 'console', DESKTOP: 'desktop', EMBEDDED: 'embedded', @@ -207,8 +217,12 @@ const Device = Object.freeze({ WEARABLE: 'wearable', XR: 'xr' }); +/** + * @deprecated Use `DeviceType` instead + */ +const Device = DeviceType; -const Vendor = Object.freeze({ +const DeviceVendor = Object.freeze({ ACER: 'Acer', ADVAN: 'Advan', ALCATEL: 'Alcatel', @@ -256,6 +270,7 @@ const Vendor = Object.freeze({ PALM: 'Palm', PANASONIC: 'Panasonic', PEBBLE: 'Pebble', + PHILIPS: 'Philips', PICO: 'Pico', POLYTRON: 'Polytron', REALME: 'Realme', @@ -270,7 +285,7 @@ const Vendor = Object.freeze({ SPRINT: 'Sprint', TCL: 'TCL', TECHNISAT: 'TechniSAT', - TECNO: 'Tecno', + TECNO: 'TECNO', TESLA: 'Tesla', ULEFONE: 'Ulefone', VIVO: 'Vivo', @@ -283,8 +298,12 @@ const Vendor = Object.freeze({ // TODO : test! }); +/** + * @deprecated Use `DeviceVendor` instead + */ +const Vendor = DeviceVendor; -const Engine = Object.freeze({ +const EngineName = Object.freeze({ AMAYA: 'Amaya', ARKWEB: 'ArkWeb', BLINK: 'Blink', @@ -306,8 +325,12 @@ const Engine = Object.freeze({ W3M: 'w3m', WEBKIT: 'WebKit' }); +/** + * @deprecated Use `EngineName` instead + */ +const Engine = EngineName; -const OS = Object.freeze({ +const OSName = Object.freeze({ AIX: 'AIX', AMIGA_OS: 'Amiga OS', ANDROID: 'Android', @@ -403,13 +426,322 @@ const OS = Object.freeze({ // TODO : test! }); +/** + * @deprecated Use `OSName` instead + */ +const OS = OSName; + +/*//////////////////////////////// + * Enums for Extensions submodule + */////////////////////////////// + +const Extension = Object.freeze({ + BrowserName: { + CLI: { + CURL: 'curl', + ELINKS: 'ELinks', + HTTPIE: 'HTTPie', + LYNX: 'Lynx', + WGET: 'Wget' + }, + Crawler: { + AHREFS_BOT: 'AhrefsBot', + AI2_BOT: 'AI2Bot', + AIHIT_BOT: 'aiHitBot', + ALGOLIA_CRAWLER: 'Algolia Crawler', + APPLE_BOT: 'Applebot', + APPLE_BOT_EXTENDED: 'Applebot-Extended', + ASK_TEOMA: 'Teoma', + AMAZON_BOT: 'Amazonbot', + AMAZON_CONTXBOT: 'contxbot', + ANTHROPIC_AI: 'anthropic-ai', + ANTHROPIC_CLAUDE_BOT: 'ClaudeBot', + ANTHROPIC_CLAUDE_SEARCHBOT: 'Claude-SearchBot', + ANTHROPIC_CLAUDE_WEB: 'Claude-Web', + ARCHIVEORG_BOT: 'archive.org_bot', + BAIDU_ADS: 'Baidu-ADS', + BAIDU_SPIDER: 'Baiduspider', + BAIDU_SPIDER_ADS: 'Baiduspider-ads', + BAIDU_SPIDER_CPRO: 'Baiduspider-cpro', + BAIDU_SPIDER_FAVO: 'Baiduspider-favo', + BAIDU_SPIDER_IMAGE: 'Baiduspider-image', + BAIDU_SPIDER_NEWS: 'Baiduspider-news', + BAIDU_SPIDER_RENDER: 'Baiduspider-render', + BAIDU_SPIDER_VIDEO: 'Baiduspider-video', + BLEX_BOT: 'BLEXBot', + BOTIFY: 'botify', + BRAVE_BOT: 'Bravebot', + BYTEDANCE_BYTESPIDER: 'Bytespider', + BYTEDANCE_TIKTOKSPIDER: 'TikTokSpider', + COMMON_CRAWL_CCBOT: 'CCBot', + COCCOC_BOT_WEB: 'coccocbot-web', + COCCOC_BOT_IMAGE: 'coccocbot-image', + COHERE_TRAINING_DATA_CRAWLER: 'cohere-training-data-crawler', + COTOYOGI: 'Cotoyogi', + COVEO_BOT: 'Coveobot', + CRITEO_BOT: 'CriteoBot', + DATAFORSEO_BOT: 'DataForSeoBot', + DAUM: 'Daum', + DAUM_DAUMOA: 'Daumoa', + DAUM_DAUMOA_IMAGE: 'Daumoa-image', + DEEPSEEK_BOT: 'DeepSeekBot', + DIFFBOT: 'Diffbot', + DUCKDUCKGO_BOT: 'DuckDuckBot', + DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot', + ELASTIC: 'Elastic', + EXALEAD_EXABOT: 'Exabot', + FIRECRAWL_AGENT: 'FirecrawlAgent', + FREESPOKE: 'Freespoke', + GOOGLE_ADSBOT: 'AdsBot-Google', + GOOGLE_ADSBOT_MOBILE: 'Adsbot-Google-Mobile', + GOOGLE_ADSENSE: 'AdSense', + GOOGLE_APIS: 'APIs-Google', + GOOGLE_BOT: 'Googlebot', + GOOGLE_BOT_IMAGE: 'Googlebot-Image', + GOOGLE_BOT_NEWS: 'Googlebot-News', + GOOGLE_BOT_VIDEO: 'Googlebot-Video', + GOOGLE_CLOUDVERTEXBOT: 'Google-CloudVertexBot', + GOOGLE_EXTENDED: 'Google-Extended', + GOOGLE_INSPECTIONTOOL: 'Google-InspectionTool', + GOOGLE_OTHER: 'GoogleOther', + GOOGLE_OTHER_IMAGE: 'GoogleOther-Image', + GOOGLE_OTHER_VIDEO: 'GoogleOther-Video', + GOOGLE_SAFETY: 'Google-Safety', + GOOGLE_STOREBOT: 'Storebot-Google', + HIVE_IMAGESIFTBOT: 'ImagesiftBot', + HUAWEI_PANGUBOT: 'PanguBot', + HUAWEI_PETALBOT: 'PetalBot', + HUGGINGFACE_BOT: 'HuggingFace-Bot', + HUNTER_VELENPUBLICWEBCRAWLER: 'VelenPublicWebCrawler', + IA_ARCHIVER: 'ia_archiver', + IASK_BOT: 'iAskBot', + KAGI_BOT: 'Kagibot', + KANGAROO_BOT: 'Kangaroo Bot', + LINE_SPIDER: 'Linespider', + LINKEDIN_BOT: 'LinkedInBot', + MAGPIE_CRAWLER: 'magpie-crawler', + MARGINALIA: 'marginalia', + META_EXTERNALAGENT: 'meta-externalagent', + META_FACEBOOKBOT: 'FacebookBot', + META_FACEBOOKCATALOG: 'facebookcatalog', + META_FACEBOOKEXTERNALHIT: 'facebookexternalhit', + MAJESTIC_MJ12BOT: 'MJ12bot', + MICROSOFT_BINGBOT: 'Bingbot', + MICROSOFT_MSNBOT: 'msnbot', + MICROSOFT_ADIDXBOT: 'adidxbot', + MOJEEK_BOT: 'MojeekBot', + MOZ_DOTBOT: 'DotBot', + ONCRAWL: 'OnCrawl', + ONESPOT_SCRAPERBOT: 'Onespot-ScraperBot', + OPENAI_GPTBOT: 'GPTBot', + OPENAI_SEARCH_BOT: 'OAI-SearchBot', + PERPLEXITY_BOT: 'PerplexityBot', + QIHOO_360_SPIDER: '360Spider', + QWANT_BOT: 'Qwantbot', + REPLICATE_BOT: 'Replicate-Bot', + RUNPOD_BOT: 'RunPod-Bot', + SB_INTUITIONS_BOT: 'SBIntuitionsBot', + SEEKPORT_BOT: 'SeekportBot', + SEMRUSH_BOT: 'SemrushBot', + SEMRUSH_BOT_BACKLINK: 'SemrushBot-BA', + SEMRUSH_BOT_CONTENTSHAKE: 'SemrushBot-OCOB', + SEMRUSH_BOT_SEO_CHECKER: 'SemrushBot-SI', + SEZNAM_BOT: 'SeznamBot', + SITEIMPROVE: 'Siteimprove', + SOGOU_PIC_SPIDER: 'Sogou Pic Spider', + SOGOU_WEB_SPIDER: 'Sogou web spider', + STARTPAGE: 'Startpage', + TIMPI_BOT: 'Timpibot', + TOGETHER_BOT: 'Together-Bot', + TURNITIN_BOT: 'TurnitinBot', + TWIN_AGENT: 'TwinAgent', + VERCEL_V0BOT: 'v0bot', + WEBZIO: 'webzio', + WEBZIO_EXTENDED: 'Webzio-Extended', + WEBZIO_OMGILI: 'omgili', + WEBZIO_OMGILI_BOT: 'omgilibot', + XAI_BOT: 'xAI-Bot', + YAHOO_JAPAN: 'Y!J-BRW', + YAHOO_SLURP: 'Yahoo! Slurp', + YANDEX_ACCESSIBILITY_BOT: 'YandexAccessibilityBot', + YANDEX_ADDITIONAL_BOT: 'YandexAdditionalBot', + YANDEX_ADNET: 'YandexAdNet', + YANDEX_BLOGS: 'YandexBlogs', + YANDEX_BOT: 'YandexBot', + YANDEX_BOT_MIRRORDETECTOR: 'YandexBot MirrorDetector', + YANDEX_COMBOT: 'YandexComBot', + YANDEX_FAVICONS: 'YandexFavicons', + YANDEX_IMAGE_RESIZER: 'YandexImageResizer', + YANDEX_IMAGES: 'YandexImages', + YANDEX_MARKET: 'YandexMarket', + YANDEX_MEDIA: 'YandexMedia', + YANDEX_METRIKA: 'YandexMetrika', + YANDEX_MOBILE_BOT: 'YandexMobileBot', + YANDEX_MOBILE_SCREENSHOT_BOT: 'YandexMobileScreenShotBot', + YANDEX_NEWS: 'YandexNews', + YANDEX_ONTODB: 'YandexOntoDB', + YANDEX_ONTODB_API: 'YandexOntoDBAPI', + YANDEX_PARTNER: 'YandexPartner', + YANDEX_RCA: 'YandexRCA', + YANDEX_RENDERRESOURCES_BOT: 'YandexRenderResourcesBot', + YANDEX_SCREENSHOT_BOT: 'YandexScreenshotBot', + YANDEX_SPRAV_BOT: 'YandexSpravBot', + YANDEX_TRACKER: 'YandexTracker', + YANDEX_VERTICALS: 'YandexVerticals', + YANDEX_VERTIS: 'YandexVertis', + YANDEX_VIDEO: 'YandexVideo', + YANDEX_VIDEO_PARSER: 'YandexVideoParser', + YANDEX_WEBMASTER: 'YandexWebmaster', + YEP_BOT: 'YepBot', + YETI: 'Yeti', + YISOU_SPIDER: 'YisouSpider', + YOU_BOT: 'YouBot', + ZHIPU_CHATGLM_SPIDER: 'ChatGLM-Spider', + ZUM_BOT: 'ZumBot' + }, + Email: { + AIRMAIL: 'Airmail', + APPLE_MAIL: 'Mail', + BLUEMAIL: 'BlueMail', + DAUM_MAIL: 'DaumMail', + EVOLUTION: 'Evolution', + EM_CLIENT: 'eM Client', + FOXMAIL: 'Foxmail', + KMAIL: 'KMail', + KMAIL2: 'kmail2', + KONTACT: 'Kontact', + MICROSOFT_OUTLOOK: 'Microsoft Outlook', + MICROSOFT_OUTLOOK_MAC: 'MacOutlook', + NAVER_MAILAPP: 'NaverMailApp', + POLYMAIL: 'Polymail', + PROTON_MAIL: 'ProtonMail', + SPARK_MAIL: 'SparkDesktop', + SPARROW: 'Sparrow', + THUNDERBIRD: 'Thunderbird', + YAHOO_MAIL: 'Yahoo', + ZIMBRA: 'Zimbra', + ZOHO_MAIL: 'ZohoMail-Desktop' + }, + Fetcher: { + AHREFS_SITEAUDIT: 'AhrefsSiteAudit', + ANTHROPIC_CLAUDE_USER: 'Claude-User', + ASANA: 'Asana', + BETTER_UPTIME_BOT: 'Better Uptime Bot', + BITLY_BOT: 'bitlybot', + BLUESKY: 'Bluesky', + BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot', + COHERE_AI: 'Cohere-AI', + DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot', + GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', + GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', + GOOGLE_GEMINI_DEEP_RESEARCH: 'Gemini-Deep-Research', + GOOGLE_IMAGEPROXY: 'GoogleImageProxy', + GOOGLE_PAGERENDERER: 'Google-PageRenderer', + GOOGLE_READ_ALOUD: 'Google-Read-Aloud', + GOOGLE_PRODUCER: 'GoogleProducer', + GOOGLE_SITE_VERIFICATION: 'Google-Site-Verification', + HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher', + IFRAMELY: 'Iframely', + KAKAOTALK_SCRAP: 'kakaotalk-scrap', + META_EXTERNALFETCHER: 'meta-externalfetcher', + META_WHATSAPP: 'WhatsApp', + MICROSOFT_BINGPREVIEW: 'BingPreview', + MICROSOFT_PREVIEW: 'MicrosoftPreview', + MISTRALAI_USER: 'MistralAI-User', + NAVER_BLUENO: 'Blueno', + ONCRAWL_ROGERBOT: 'rogerbot', + OPENAI_CHATGPT_USER: 'ChatGPT-User', + PERPLEXITY_USER: 'Perplexity-User', + PINTEREST_BOT: 'Pinterestbot', + SEMRUSH_SITEAUDITBOT: 'SiteAuditBot', + SNAP_URL_PREVIEW: 'Snap URL Preview', + SKYPE_URIPREVIEW: 'SkypeUriPreview', + TELEGRAM_BOT: 'TelegramBot', + UPTIMEROBOT: 'UptimeRobot', + VERCEL_FAVICON_BOT: 'vercel-favicon-bot', + VERCEL_SCREENSHOT_BOT: 'vercel-screenshot-bot', + VERCEL_BOT: 'Vercelbot', + VERCEL_FLAGS: 'vercelflags', + VERCEL_TRACING: 'verceltracing', + YANDEX_CALENDAR: 'YandexCalendar', + YANDEX_DIRECT: 'YandexDirect', + YANDEX_DIRECTDYN: 'YandexDirectDyn', + YANDEX_DIRECTFETCHER: 'YaDirectFetcher', + YANDEX_FORDOMAIN: 'YandexForDomain', + YANDEX_PAGECHECKER: 'YandexPagechecker', + YANDEX_SEARCHSHOP: 'YandexSearchShop', + YANDEX_SITELINKS: 'YandexSitelinks', + YANDEX_USERPROXY: 'YandexUserproxy', + ZOOMINFO_BOT: 'Zoombot' + }, + InApp: { + DISCORD: 'Discord', + EVERNOTE: 'Evernote', + FIGMA: 'Figma', + FLIPBOARD: 'Flipboard', + MATTERMOST: 'Mattermost', + TEAMS: 'Teams', + NOTION: 'Notion', + POSTMAN: 'Postman', + RAMBOX: 'Rambox', + ROCKETCHAT: 'Rocket.Chat', + SLACK: 'Slack', + TIKTOK_LITE: 'TikTok Lite', + VSCODE: 'VS Code', + YAHOO_JAPAN: 'Yahoo! Japan' + }, + Library: { + ADOBE_AIR: 'AdobeAIR', + AIOHTTP: 'aiohttp', + APACHE_HTTPCLIENT: 'Apache-HttpClient', + AXIOS: 'axios', + GO_HTTP_CLIENT: 'go-http-client', + GOT: 'got', + GUZZLEHTTP: 'GuzzleHttp', + JAVA: 'Java', + JAVA_HTTPCLIENT: 'Java-http-client', + JSDOM: 'jsdom', + LIBWWW_PERL: 'libwww-perl', + LUA_RESTY_HTTP: 'lua-resty-http', + NEEDLE: 'Needle', + NUTCH: 'Nutch', + OKHTTP: 'OkHttp', + NODE_FETCH: 'node-fetch', + NODE_SUPERAGENT: 'node-superagent', + PHP_SOAP: 'PHP-SOAP', + POSTMAN_RUNTIME: 'PostmanRuntime', + PYTHON_HTTPX: 'python-httpx', + PYTHON_URLLIB: 'python-urllib', + PYTHON_URLLIB3: 'python-urllib3', + PYTHON_REQUESTS: 'python-requests', + SCRAPY: 'Scrapy' + } + }, + DeviceVendor: { + Vehicle: { + BMW: 'BMW', + BYD: 'BYD', + JEEP: 'Jeep', + RIVIAN: 'Rivian', + TESLA: 'Tesla', + VOLVO: 'Volvo' + } + } +}); export { - Browser, + Browser,// deprecated + CPU, // deprecated + Device, // deprecated + Vendor, // deprecated + Engine, // deprecated + OS, // deprecated + BrowserName, BrowserType, - CPU, - Device, - Vendor, - Engine, - OS + CPUArch, + DeviceType, + DeviceVendor, + EngineName, + OSName, + Extension }; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts index 4ee31dd..3438970 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.4 +// Type definitions for Helpers submodule of UAParser.js v2.0.5 // 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 cc6f8d0..2caf129 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.4 +/* Extensions for UAParser.js v2.0.5 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -283,8 +283,8 @@ const Fetchers = Object.freeze({ [NAME, VERSION, [TYPE, FETCHER]], [ - // Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / Snapchat / TikTokSpider / Vercelbot / Yandex Bots - /((?:better uptime |telegram|vercel)bot|chrome-lighthouse|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 + // Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / Snapchat / Vercelbot / Yandex Bots + /((?:better uptime |telegram|vercel)bot|chrome-lighthouse|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i ], [NAME, [TYPE, FETCHER]], ], diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs index 9401493..2260057 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.4 +/* Extensions for UAParser.js v2.0.5 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -47,20 +47,31 @@ 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 + // contxbot - https://affiliate-program.amazon.com/help/node/topic/GT98G5PPRERNVZ2C + // 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/ // GPTBot - https://platform.openai.com/docs/gptbot // iAskBot - https://iask.ai + // Kagibot - https://kagi.com/bot + // Kangaroo Bot - https://kangaroollm.com.au/kangaroo-bot/ // 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 + // SBIntuitionsBot - https://www.sbintuitions.co.jp/bot/ // 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, + // YepBot - https://yep.com/yepbot/ + /((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|sbintuitions|semrush|seznam|yep)bot)\/([\w\.-]+)/i, + + // Algolia Crawler + /(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i, // Applebot - http://apple.com/go/applebot /(applebot(?:-extended)?)\/?([\w\.]*)/i, @@ -69,7 +80,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, @@ -87,6 +98,9 @@ const Crawlers = Object.freeze({ // Internet Archive (archive.org) /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + // OnCrawl + /(oncrawl) mobile\/([\w\.]+)/i, + // Qwantbot - https://help.qwant.com/bot /(qwantbot)[-\w]*\/?([\w\.]*)/i, @@ -100,30 +114,38 @@ 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|com|renderresources|screenshot|sprav)?bot(?!.+mirror)|image(?:s|resizer)|adnet|blogs|favicons|market|media|metrika|news|ontodb(?:api)?|partner|rca|tracker|turbo|verti(?:cal)?s|webmaster|video(?:parser)?))\/([\w\.]+)/i, // 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 / Algolia Crawler / BLEXBot / Diffbot / FirecrawlAgent / HuggingFace-Bot / Linespider / MSNBot / 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 / ZumBot + // Cotoyogi - https://ds.rois.ac.jp/en_center8/en_crawler/ + // Freespoke - https://docs.freespoke.com/search/bot/ + /((?:aihit|blex|diff|huggingface-|msn|pangu|replicate-|runpod-|timpi|together-|xai-|you|zum)bot|(?:magpie-|velenpublicweb)crawler|(?:chatglm-|line|screaming frog seo |yisou)spider|cotoyogi|firecrawlagent|freespoke|omgili(?:bot)?|openai image downloader|startpageprivateimageproxy|twinagent|webzio-extended)\/?([\w\.]*)/i ], - [NAME, VERSION, [TYPE, CRAWLER]], + [ + // YandexBot MirrorDetector + /(yandexbot\/([\w\.]+); mirrordetector)/i + ], + [[NAME, /\/.+;/ig, ''], VERSION, [TYPE, CRAWLER]], + [ // 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 + // Siteimprove - https://help.siteimprove.com/support/solutions/articles/80000448553 // 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|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|yahoo! slurp)/i + // Botify / Bytespider / DeepSeekBot / Qihoo 360Spider / SeekportBot / TikTokSpider + /\b((ai2|aspiegel|dataforseo|deepseek|imagesift|petal|seekport|turnitin|v0)bot|360spider-?(image|video)?|baidu-ads|botify|(byte|tiktok)spider|cohere-training-data-crawler|elastic(?=\/s)|marginalia|siteimprove(?=bot|\.com)|teoma|webzio|yahoo! slurp)/i ], [NAME, [TYPE, CRAWLER]] ] @@ -238,16 +260,17 @@ const Emails = Object.freeze({ const Fetchers = Object.freeze({ browser : [ [ + // Asana / Bitlybot / Better Uptime / BingPreview / Blueno / Cohere-AI / HubSpot Page Fetcher / 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 / 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, + /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|hubspot page fetcher|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)?|fordomain|pagechecker|searchshop)|yadirectfetcher)\/([\w\.]+)/i, // Bluesky /(bluesky) cardyb\/([\w\.]+)/i, @@ -264,8 +287,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 / Gemini-Deep-Research / Snapchat / Vercelbot / Yandex Bots + /((?:better uptime |telegram|vercel)bot|chrome-lighthouse|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i ], [NAME, [TYPE, FETCHER]], ], @@ -408,8 +431,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.d.ts b/src/helpers/ua-parser-helpers.d.ts index c860fd2..caee9cb 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.4 +// Type definitions for Helpers submodule of UAParser.js v2.0.5 // 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 54ce86f..b19d0bf 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.4 +/* Helpers for UAParser.js v2.0.5 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 3530e7d..5cdcdf0 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.4 +/* Helpers for UAParser.js v2.0.5 https://github.com/faisalman/ua-parser-js Author: Faisal Salman AGPLv3 License */ @@ -12,11 +12,12 @@ /*jshint esversion: 6 */ 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 { CPUArch, OSName, EngineName, Extension, BrowserType } from '../enums/ua-parser-enums.mjs'; +import { Bots, Crawlers } 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 { Crawler } = Extension.BrowserName; const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value; @@ -24,8 +25,8 @@ const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${m const isAppleSilicon = (resultOrUA) => { const res = toResult(resultOrUA); - if (res.os.is(OS.MACOS)) { - if (res.cpu.is(CPU.ARM)) { + if (res.os.is(OSName.MACOS)) { + if (res.cpu.is(CPUArch.ARM)) { return true; } if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') { @@ -48,88 +49,129 @@ const isAppleSilicon = (resultOrUA) => { const isAIBot = (resultOrUA) => [ // AI2 - 'ai2bot', + Crawler.AI2_BOT, // Amazon - 'amazonbot', + Crawler.AMAZON_BOT, // Anthropic - 'anthropic-ai', - 'claude-web', - 'claudebot', + Crawler.ANTHROPIC_AI, + Crawler.ANTHROPIC_CLAUDE_BOT, + Crawler.ANTHROPIC_CLAUDE_SEARCHBOT, + Crawler.ANTHROPIC_CLAUDE_WEB, // Apple - 'applebot', - 'applebot-extended', + Crawler.APPLE_BOT, + Crawler.APPLE_BOT_EXTENDED, + + // Brave + Crawler.BRAVE_BOT, // ByteDance - 'bytespider', + Crawler.BYTEDANCE_BYTESPIDER, + Crawler.BYTEDANCE_TIKTOKSPIDER, + + // Cohere + Crawler.COHERE_TRAINING_DATA_CRAWLER, // Common Crawl - 'ccbot', + Crawler.COMMON_CRAWL_CCBOT, + + // Coveo + Crawler.COVEO_BOT, // DataForSeo - 'dataforseobot', + Crawler.DATAFORSEO_BOT, + + // DeepSeek + Crawler.DEEPSEEK_BOT, // Diffbot - 'diffbot', + Crawler.DIFFBOT, // Google - 'googleother', - 'googleother-image', - 'googleother-video', - 'google-extended', + Crawler.GOOGLE_EXTENDED, + Crawler.GOOGLE_OTHER, + Crawler.GOOGLE_OTHER_IMAGE, + Crawler.GOOGLE_OTHER_VIDEO, + Crawler.GOOGLE_CLOUDVERTEXBOT, // Hive AI - 'imagesiftbot', + Crawler.HIVE_IMAGESIFTBOT, // Huawei - 'petalbot', + Crawler.HUAWEI_PETALBOT, + Crawler.HUAWEI_PANGUBOT, + + // Hugging Face + Crawler.HUGGINGFACE_BOT, + + // Kangaroo + Crawler.KANGAROO_BOT, + + // Mendable.ai + Crawler.FIRECRAWL_AGENT, // Meta - 'facebookbot', - 'meta-externalagent', + Crawler.META_FACEBOOKBOT, + Crawler.META_EXTERNALAGENT, // OpenAI - 'gptbot', - 'oai-searchbot', + Crawler.OPENAI_GPTBOT, + Crawler.OPENAI_SEARCH_BOT, // Perplexity - 'perplexitybot', + Crawler.PERPLEXITY_BOT, + + // Replicate + Crawler.REPLICATE_BOT, + + // Runpod + Crawler.RUNPOD_BOT, + + // SB Intuitions + Crawler.SB_INTUITIONS_BOT, // Semrush - 'semrushbot-ocob', + Crawler.SEMRUSH_BOT_CONTENTSHAKE, // Timpi - 'timpibot', + Crawler.TIMPI_BOT, + + // Together AI + Crawler.TOGETHER_BOT, // Velen.io - 'velenpublicwebcrawler', + Crawler.HUNTER_VELENPUBLICWEBCRAWLER, + + // Vercel + Crawler.VERCEL_V0BOT, // Webz.io - 'omgili', - 'omgilibot', - 'webzio-extended', + Crawler.WEBZIO_OMGILI, + Crawler.WEBZIO_OMGILI_BOT, + Crawler.WEBZIO_EXTENDED, + + // X + Crawler.XAI_BOT, // You.com - 'youbot', + Crawler.YOU_BOT, // Zhipu AI - 'chatglm-spider', - - // Zyte - 'scrapy' - - ].includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + Crawler.ZHIPU_CHATGLM_SPIDER + ] + .map((s) => s.toLowerCase()) + .includes(String(toResult(resultOrUA, Crawlers).browser.name).toLowerCase()); const isBot = (resultOrUA) => [ - 'cli', - 'crawler', - 'fetcher', - 'library' + BrowserType.CLI, + BrowserType.CRAWLER, + BrowserType.FETCHER, + BrowserType.LIBRARY ].includes(toResult(resultOrUA, Bots).browser.type); -const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(Engine.BLINK); +const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(EngineName.BLINK); const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js / electron\//i.test(navigator?.userAgent)); // browser diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts index 6fd0ef5..09c11ee 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.4 +// Type definitions for UAParser.js v2.0.5 // 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 2030ae8..bc0ef93 100755 --- a/src/main/ua-parser.js +++ b/src/main/ua-parser.js @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v2.0.4 +/* UAParser.js v2.0.5 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.4', + var LIBVERSION = '2.0.5', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs index 2b45cda..04545da 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.4 +/* UAParser.js v2.0.5 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.4', + var LIBVERSION = '2.0.5', UA_MAX_LENGTH = 500, USER_AGENT = 'user-agent', EMPTY = '', @@ -459,6 +459,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], [ @@ -540,15 +542,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 : [[ @@ -772,7 +774,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 +802,7 @@ // SMARTTVS /////////////////// + /(philips)[\w ]+tv/i, // Philips /smart-tv.+(samsung)/i // Samsung ], [VENDOR, [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i @@ -836,11 +840,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 @@ -887,7 +886,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]], [ @@ -911,6 +910,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 @@ -1389,11 +1392,22 @@ 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 (headers) { + if (typeof headers.append === FUNC_TYPE) { + // Convert Headers object into a plain object + var kv = {}; + headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; }); + headers = kv; + } else { + // Normalize headers field name into lowercase + var normalized = {}; + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + normalized[String(header).toLowerCase()] = headers[header]; + } + } + headers = normalized; + } } if (!(this instanceof UAParser)) { diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json index 7b0bd4e..373b402 100644 --- a/test/data/ua/extension/fetcher.json +++ b/test/data/ua/extension/fetcher.json @@ -339,16 +339,6 @@ "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/)",
License optionsMIT (v0.7~v1.0)AGPL (>=v2.0)MIT (v1.x)AGPL (v2.x) PRO Personal PRO Business PRO Enterprise
npm module
TypeScript declarations ⚠️
Allows commercial usenpm module available
Direct downloads available
Allows commercial usage
Unlimited use per 1 licenseNo open-source obligations
Unlimited end-products
Unlimited deployments
PriceFREE (License)FREE (License)FREE* (License)FREE* (License) $14 (License) $29 (License) $599 (License)