1
0
mirror of https://github.com/lancedikson/bowser synced 2025-12-05 06:02:14 +00:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Copilot
0f51d8bce8
Replace lancedikson with bowser-js in GitHub URLs (#592)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com>
2025-11-30 18:58:23 +02:00
Naor Peled
1df8838756
revert: rollback addition of named exports (#591) 2025-11-29 02:01:44 +02:00
naorpeled
eef8480944 docs(package.json): add naorpeled as contributor 2025-11-23 00:08:32 +02:00
Naor Peled
514510d847
feat: add support for latest MacOS (#580)
Co-authored-by: Seiji Kohara <9543980+seijikohara@users.noreply.github.com>
2025-11-22 23:50:54 +02:00
Naor Peled
4a6dacca08
feat: add Sogou browser support (#579)
Co-authored-by: 猫猫 <49067249+NotEvenANeko@users.noreply.github.com>
2025-11-22 23:31:31 +02:00
Copilot
be90a00e37
feat: add LibreWolf browser detection (#578)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com>
2025-11-22 19:01:03 +02:00
Naor Peled
f7d2c0693c
feat: add support for AI crawl bots (#577) 2025-11-22 18:05:08 +02:00
Lucio Martinez
771dfb2dfe
feat: add more bot platforms (#542)
Co-authored-by: naorpeled <me@naor.dev>
2025-11-22 16:44:08 +02:00
Copilot
bd5cb7186c
feat: add named exports for tree shaking support with modern bundlers (#566)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com>
Co-authored-by: naorpeled <me@naor.dev>
2025-11-22 15:15:13 +02:00
Copilot
2b5ee5de8c
feat: add Support for HarmonyOS User-Agent Detection (#567)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com>
2025-11-22 14:27:00 +02:00
Copilot
a4d0b828e4
chore: add Copilot instructions for repository (#574)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com>
2025-11-22 13:31:12 +02:00
Naor Peled
5bfd3e9e6c
revert(index.d.ts): rollback default export to = syntax (#570) 2025-08-23 19:32:29 +03:00
Naor Peled
3c25806efe ci(publish): add manual trigger and retry logic for npm publish 2025-08-09 21:27:38 +03:00
15 changed files with 1483 additions and 60 deletions

129
.github/copilot-instructions.md vendored Normal file
View File

@ -0,0 +1,129 @@
# Copilot Instructions for Bowser
## Project Overview
Bowser is a small, fast, and rich-API browser/platform/engine detector for both browser and Node.js environments. It's designed to parse User-Agent strings and provide detailed information about browsers, operating systems, platforms, and rendering engines.
## Architecture
### Core Components
- **`src/bowser.js`**: Main entry point and public API. Provides static methods `getParser()` and `parse()`.
- **`src/parser.js`**: Core parsing engine that orchestrates all parsers and returns structured results.
- **`src/parser-browsers.js`**: Browser detection logic using regex patterns.
- **`src/parser-os.js`**: Operating system detection logic.
- **`src/parser-platforms.js`**: Platform type detection (desktop, tablet, mobile).
- **`src/parser-engines.js`**: Rendering engine detection (WebKit, Blink, Gecko, etc.).
- **`src/constants.js`**: Centralized constants including browser aliases and mappings.
- **`src/utils.js`**: Utility functions for string matching and manipulation.
### Build Output
- **`es5.js`**: ES5 transpiled version (default export).
- **`bundled.js`**: ES5 version with babel-polyfill included.
## Development Workflow
### Setup
```bash
npm install
```
### Key Commands
- **Build**: `npm run build` - Compiles source files using Webpack and Babel.
- **Test**: `npm test` - Runs unit and acceptance tests using AVA.
- **Lint**: `npm run lint:check` - Checks code style using ESLint.
- **Lint Fix**: `npm run lint:fix` - Auto-fixes linting issues.
- **Watch Mode**: `npm run watch` - Builds on file changes.
- **Test Watch**: `npm run test:watch` - Runs tests on file changes.
### Testing
- Tests are located in `test/acceptance/` and `test/unit/`.
- Acceptance tests use real User-Agent strings from `test/acceptance/useragentstrings.yml`.
- Always update `useragentstrings.yml` when adding browser support.
- Test framework: AVA with Babel integration.
## Coding Standards
### Style Guide
- **ESLint Config**: Based on Airbnb Base style guide.
- **Parser**: Uses `babel-eslint`.
- **Exceptions**:
- Underscore-dangle allowed for private properties.
- `no-void` disabled.
- ES6 imports must include `.js` extension.
### Naming Conventions
- **Browser Aliases**: Use lowercase letters, replace spaces/dashes with underscores, drop "browser" suffix.
- Examples: `Opera Coast``opera_coast`, `UC Browser``uc`, `SeaMonkey``seamonkey`.
- **Private Properties**: Prefix with underscore (e.g., `_ua`).
- **Constants**: Use `UPPER_SNAKE_CASE` for constant maps and aliases.
### Code Patterns
- Use ES6 modules with explicit `.js` extensions.
- Prefer static methods in Bowser class.
- Use class-based structure for Parser.
- Regex patterns should be well-documented and tested.
- Keep parsers modular and focused on single responsibility.
## Adding Browser Support
When adding support for a new browser:
1. Add regex pattern to `src/parser-browsers.js`.
2. Add browser name to `BROWSER_ALIASES_MAP` in `src/constants.js`.
3. Add corresponding entry to `BROWSER_MAP`.
4. Add test cases to `test/acceptance/useragentstrings.yml`.
5. Run tests to verify: `npm test`.
6. Check for duplicates before adding aliases.
## Branching Strategy
- **`master`**: Development branch.
- **`production`**: Production branch.
- **New Features**: Branch from `master`, PR back to `master`.
- **Hot-fixes/Browser Support**: Branch from `production`, PR back to `production`.
## Important Files
- **`index.d.ts`**: TypeScript definitions.
- **`.babelrc`**: Babel configuration for ES5 transpilation.
- **`webpack.config.js`**: Build configuration.
- **`.eslintrc.yml`**: Linting rules.
- **`package.json`**: Dependencies and scripts.
## API Design Principles
- Keep the API simple and intuitive.
- Bowser class should be stateless and provide factory methods.
- Parser class handles instance-specific logic.
- Results should be structured and predictable.
- Support both immediate parsing and lazy parsing.
## Performance Considerations
- Parsers use lazy evaluation where possible.
- Regex patterns are optimized for common browsers first.
- Optional `skipParsing` parameter for delayed parsing.
- Minimal bundle size is a priority (~4.8kB gzipped).
## Documentation
- Use JSDoc comments for all public APIs.
- Document parameters, return types, and provide examples.
- Update README.md for API changes.
- Generate docs with: `npm run generate-docs`.
## Common Pitfalls
- Always check `BROWSER_ALIASES_MAP` for existing aliases before adding new ones.
- User-Agent strings can be complex; test edge cases thoroughly.
- Remember to update both the alias map and the reverse map in constants.
- Browser versions should be treated as strings, not numbers.
- Keep regex patterns readable with comments explaining their purpose.

View File

@ -4,6 +4,13 @@ on:
# This job runs when a new release is published # This job runs when a new release is published
release: release:
types: [published] types: [published]
# Manual trigger with version input
workflow_dispatch:
inputs:
version:
description: "Version to release (e.g., 2.12.1)"
required: true
type: string
jobs: jobs:
release: release:
@ -13,16 +20,44 @@ jobs:
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 12.16.3 node-version: 12.16.3
registry-url: "https://registry.npmjs.org"
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: ~/.npm path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }}
# Store the name of the release # Store the release version (from release tag or manual input)
# See https://stackoverflow.com/questions/58177786/get-the-current-pushed-tag-in-github-actions - name: Set release version
- run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "RELEASE_VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
echo "Manual release triggered for version: ${{ github.event.inputs.version }}"
else
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
echo "Release triggered from tag: ${GITHUB_REF#refs/*/}"
fi
- run: npm ci - run: npm ci
- run: npm version $RELEASE_VERSION --no-git-tag-version - run: npm version $RELEASE_VERSION --no-git-tag-version
- run: npm run build - run: npm run build
- run: npm publish --access public - name: Publish to npm with retry
run: |
max_attempts=5
attempt=1
while [ $attempt -le $max_attempts ]; do
echo "Attempt $attempt of $max_attempts..."
if npm publish --access public; then
echo "Successfully published!"
break
else
if [ $attempt -eq $max_attempts ]; then
echo "Failed to publish after $max_attempts attempts"
exit 1
fi
echo "Publish failed, waiting before retry..."
sleep_time=$((attempt * 30))
echo "Waiting ${sleep_time} seconds before retry..."
sleep $sleep_time
attempt=$((attempt + 1))
fi
done
env: env:
NODE_AUTH_TOKEN: ${{ secrets.BOWSER_NPM_PUBLISH_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.BOWSER_NPM_PUBLISH_TOKEN }}

View File

@ -6,7 +6,7 @@ A small, fast and rich-API browser/platform/engine detector for both browser and
Don't hesitate to support the project on Github or [OpenCollective](https://opencollective.com/bowser) if you like it ❤️ Also, contributors are always welcome! Don't hesitate to support the project on Github or [OpenCollective](https://opencollective.com/bowser) if you like it ❤️ Also, contributors are always welcome!
[![Financial Contributors on Open Collective](https://opencollective.com/bowser/all/badge.svg?label=financial+contributors)](https://opencollective.com/bowser) [![Build Status](https://travis-ci.org/lancedikson/bowser.svg?branch=master)](https://travis-ci.org/lancedikson/bowser/) [![Greenkeeper badge](https://badges.greenkeeper.io/lancedikson/bowser.svg)](https://greenkeeper.io/) [![Coverage Status](https://coveralls.io/repos/github/lancedikson/bowser/badge.svg?branch=master)](https://coveralls.io/github/lancedikson/bowser?branch=master) ![Downloads](https://img.shields.io/npm/dm/bowser) [![Financial Contributors on Open Collective](https://opencollective.com/bowser/all/badge.svg?label=financial+contributors)](https://opencollective.com/bowser) [![Build Status](https://travis-ci.org/bowser-js/bowser.svg?branch=master)](https://travis-ci.org/bowser-js/bowser/) [![Greenkeeper badge](https://badges.greenkeeper.io/bowser-js/bowser.svg)](https://greenkeeper.io/) [![Coverage Status](https://coveralls.io/repos/github/bowser-js/bowser/badge.svg?branch=master)](https://coveralls.io/github/bowser-js/bowser?branch=master) ![Downloads](https://img.shields.io/npm/dm/bowser)
# Contents # Contents
- [Overview](#overview) - [Overview](#overview)
@ -20,7 +20,7 @@ The library is made to help to detect what browser your user has and gives you a
Version 2.0 has drastically changed the API. All available methods are on the [docs page](https://bowser-js.github.io/bowser/docs/). Version 2.0 has drastically changed the API. All available methods are on the [docs page](https://bowser-js.github.io/bowser/docs/).
_For legacy code, check out the [1.x](https://github.com/lancedikson/bowser/tree/v1.x) branch and install it through `npm install bowser@1.9.4`._ _For legacy code, check out the [1.x](https://github.com/bowser-js/bowser/tree/v1.x) branch and install it through `npm install bowser@1.9.4`._
# Use cases # Use cases
@ -148,7 +148,7 @@ list of aliases can be found in [the file](src/constants.js).
### Code Contributors ### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](.github/CONTRIBUTING.md)]. This project exists thanks to all the people who contribute. [[Contribute](.github/CONTRIBUTING.md)].
<a href="https://github.com/lancedikson/bowser/graphs/contributors"><img src="https://opencollective.com/bowser/contributors.svg?width=890&button=false" /></a> <a href="https://github.com/bowser-js/bowser/graphs/contributors"><img src="https://opencollective.com/bowser/contributors.svg?width=890&button=false" /></a>
### Financial Contributors ### Financial Contributors

View File

@ -1226,7 +1226,12 @@
10.12 - Sierra 10.12 - Sierra
10.13 - High Sierra 10.13 - High Sierra
10.14 - Mojave 10.14 - Mojave
10.15 - Catalina</p> 10.15 - Catalina
11 - Big Sur
12 - Monterey
13 - Ventura
14 - Sonoma
15 - Sequoia</p>
</div> </div>

View File

@ -110,6 +110,11 @@ export default class Utils {
* 10.13 - High Sierra * 10.13 - High Sierra
* 10.14 - Mojave * 10.14 - Mojave
* 10.15 - Catalina * 10.15 - Catalina
* 11 - Big Sur
* 12 - Monterey
* 13 - Ventura
* 14 - Sonoma
* 15 - Sequoia
* *
* @example * @example
* getMacOSVersionName("10.14") // 'Mojave' * getMacOSVersionName("10.14") // 'Mojave'
@ -120,8 +125,11 @@ export default class Utils {
static getMacOSVersionName(version) { static getMacOSVersionName(version) {
const v = version.split('.').splice(0, 2).map(s => parseInt(s, 10) || 0); const v = version.split('.').splice(0, 2).map(s => parseInt(s, 10) || 0);
v.push(0); v.push(0);
if (v[0] !== 10) return undefined; const major = v[0];
switch (v[1]) { const minor = v[1];
if (major === 10) {
switch (minor) {
case 5: return 'Leopard'; case 5: return 'Leopard';
case 6: return 'Snow Leopard'; case 6: return 'Snow Leopard';
case 7: return 'Lion'; case 7: return 'Lion';
@ -137,6 +145,16 @@ export default class Utils {
} }
} }
switch (major) {
case 11: return 'Big Sur';
case 12: return 'Monterey';
case 13: return 'Ventura';
case 14: return 'Sonoma';
case 15: return 'Sequoia';
default: return undefined;
}
}
/** /**
* Get Android version name * Get Android version name
* 1.5 - Cupcake * 1.5 - Cupcake

4
index.d.ts vendored
View File

@ -1,8 +1,8 @@
// Type definitions for Bowser v2 // Type definitions for Bowser v2
// Project: https://github.com/lancedikson/bowser // Project: https://github.com/bowser-js/bowser
// Definitions by: Alexander P. Cerutti <https://github.com/alexandercerutti>, // Definitions by: Alexander P. Cerutti <https://github.com/alexandercerutti>,
export default Bowser; export = Bowser;
export as namespace Bowser; export as namespace Bowser;
declare namespace Bowser { declare namespace Bowser {

View File

@ -11,12 +11,16 @@
"ender", "ender",
"sniff" "sniff"
], ],
"homepage": "https://github.com/lancedikson/bowser", "homepage": "https://github.com/bowser-js/bowser",
"author": "Dustin Diaz <dustin@dustindiaz.com> (http://dustindiaz.com)", "author": "Dustin Diaz <dustin@dustindiaz.com> (http://dustindiaz.com)",
"contributors": [ "contributors": [
{ {
"name": "Denis Demchenko", "name": "Denis Demchenko",
"url": "http://twitter.com/lancedikson" "url": "http://twitter.com/lancedikson"
},
{
"name": "Naor Peled",
"url": "https://github.com/naorpeled"
} }
], ],
"main": "es5.js", "main": "es5.js",
@ -25,7 +29,7 @@
"types": "index.d.ts", "types": "index.d.ts",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/lancedikson/bowser.git" "url": "git+https://github.com/bowser-js/bowser.git"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.11.6", "@babel/cli": "^7.11.6",
@ -61,7 +65,7 @@
] ]
}, },
"bugs": { "bugs": {
"url": "https://github.com/lancedikson/bowser/issues" "url": "https://github.com/bowser-js/bowser/issues"
}, },
"directories": { "directories": {
"test": "test" "test": "test"

View File

@ -1,6 +1,6 @@
/*! /*!
* Bowser - a browser detector * Bowser - a browser detector
* https://github.com/lancedikson/bowser * https://github.com/bowser-js/bowser
* MIT License | (c) Dustin Diaz 2012-2015 * MIT License | (c) Dustin Diaz 2012-2015
* MIT License | (c) Denis Demchenko 2015-2019 * MIT License | (c) Denis Demchenko 2015-2019
*/ */

View File

@ -1,119 +1,169 @@
// NOTE: this list must be up-to-date with browsers listed in // NOTE: this list must be up-to-date with browsers listed in
// test/acceptance/useragentstrings.yml // test/acceptance/useragentstrings.yml
export const BROWSER_ALIASES_MAP = { export const BROWSER_ALIASES_MAP = {
AmazonBot: 'amazonbot',
'Amazon Silk': 'amazon_silk', 'Amazon Silk': 'amazon_silk',
'Android Browser': 'android', 'Android Browser': 'android',
BaiduSpider: 'baiduspider',
Bada: 'bada', Bada: 'bada',
BingCrawler: 'bingcrawler',
BlackBerry: 'blackberry', BlackBerry: 'blackberry',
'ChatGPT-User': 'chatgpt_user',
Chrome: 'chrome', Chrome: 'chrome',
ClaudeBot: 'claudebot',
Chromium: 'chromium', Chromium: 'chromium',
Diffbot: 'diffbot',
DuckDuckBot: 'duckduckbot',
Electron: 'electron', Electron: 'electron',
Epiphany: 'epiphany', Epiphany: 'epiphany',
FacebookExternalHit: 'facebookexternalhit',
Firefox: 'firefox', Firefox: 'firefox',
Focus: 'focus', Focus: 'focus',
Generic: 'generic', Generic: 'generic',
'Google Search': 'google_search', 'Google Search': 'google_search',
Googlebot: 'googlebot', Googlebot: 'googlebot',
GPTBot: 'gptbot',
'Internet Explorer': 'ie', 'Internet Explorer': 'ie',
InternetArchiveCrawler: 'internetarchivecrawler',
'K-Meleon': 'k_meleon', 'K-Meleon': 'k_meleon',
LibreWolf: 'librewolf',
Maxthon: 'maxthon', Maxthon: 'maxthon',
'Meta-ExternalAds': 'meta_externalads',
'Meta-ExternalAgent': 'meta_externalagent',
'Meta-ExternalFetcher': 'meta_externalfetcher',
'Meta-WebIndexer': 'meta_webindexer',
'Microsoft Edge': 'edge', 'Microsoft Edge': 'edge',
'MZ Browser': 'mz', 'MZ Browser': 'mz',
'NAVER Whale Browser': 'naver', 'NAVER Whale Browser': 'naver',
'OAI-SearchBot': 'oai_searchbot',
Omgilibot: 'omgilibot',
Opera: 'opera', Opera: 'opera',
'Opera Coast': 'opera_coast', 'Opera Coast': 'opera_coast',
'Pale Moon': 'pale_moon', 'Pale Moon': 'pale_moon',
PerplexityBot: 'perplexitybot',
'Perplexity-User': 'perplexity_user',
PhantomJS: 'phantomjs', PhantomJS: 'phantomjs',
PingdomBot: 'pingdombot',
Puffin: 'puffin', Puffin: 'puffin',
QupZilla: 'qupzilla',
QQ: 'qq', QQ: 'qq',
QQLite: 'qqlite', QQLite: 'qqlite',
QupZilla: 'qupzilla',
Roku: 'roku',
Safari: 'safari', Safari: 'safari',
Sailfish: 'sailfish', Sailfish: 'sailfish',
'Samsung Internet for Android': 'samsung_internet', 'Samsung Internet for Android': 'samsung_internet',
SeaMonkey: 'seamonkey', SeaMonkey: 'seamonkey',
Sleipnir: 'sleipnir', Sleipnir: 'sleipnir',
'Sogou Browser': 'sogou',
Swing: 'swing', Swing: 'swing',
Tizen: 'tizen', Tizen: 'tizen',
'UC Browser': 'uc', 'UC Browser': 'uc',
Vivaldi: 'vivaldi', Vivaldi: 'vivaldi',
'WebOS Browser': 'webos', 'WebOS Browser': 'webos',
WeChat: 'wechat', WeChat: 'wechat',
YahooSlurp: 'yahooslurp',
'Yandex Browser': 'yandex', 'Yandex Browser': 'yandex',
Roku: 'roku', YandexBot: 'yandexbot',
YouBot: 'youbot',
}; };
export const BROWSER_MAP = { export const BROWSER_MAP = {
amazonbot: 'AmazonBot',
amazon_silk: 'Amazon Silk', amazon_silk: 'Amazon Silk',
android: 'Android Browser', android: 'Android Browser',
baiduspider: 'BaiduSpider',
bada: 'Bada', bada: 'Bada',
bingcrawler: 'BingCrawler',
blackberry: 'BlackBerry', blackberry: 'BlackBerry',
chatgpt_user: 'ChatGPT-User',
chrome: 'Chrome', chrome: 'Chrome',
claudebot: 'ClaudeBot',
chromium: 'Chromium', chromium: 'Chromium',
diffbot: 'Diffbot',
duckduckbot: 'DuckDuckBot',
edge: 'Microsoft Edge',
electron: 'Electron', electron: 'Electron',
epiphany: 'Epiphany', epiphany: 'Epiphany',
facebookexternalhit: 'FacebookExternalHit',
firefox: 'Firefox', firefox: 'Firefox',
focus: 'Focus', focus: 'Focus',
generic: 'Generic', generic: 'Generic',
googlebot: 'Googlebot',
google_search: 'Google Search', google_search: 'Google Search',
googlebot: 'Googlebot',
gptbot: 'GPTBot',
ie: 'Internet Explorer', ie: 'Internet Explorer',
internetarchivecrawler: 'InternetArchiveCrawler',
k_meleon: 'K-Meleon', k_meleon: 'K-Meleon',
librewolf: 'LibreWolf',
maxthon: 'Maxthon', maxthon: 'Maxthon',
edge: 'Microsoft Edge', meta_externalads: 'Meta-ExternalAds',
meta_externalagent: 'Meta-ExternalAgent',
meta_externalfetcher: 'Meta-ExternalFetcher',
meta_webindexer: 'Meta-WebIndexer',
mz: 'MZ Browser', mz: 'MZ Browser',
naver: 'NAVER Whale Browser', naver: 'NAVER Whale Browser',
oai_searchbot: 'OAI-SearchBot',
omgilibot: 'Omgilibot',
opera: 'Opera', opera: 'Opera',
opera_coast: 'Opera Coast', opera_coast: 'Opera Coast',
pale_moon: 'Pale Moon', pale_moon: 'Pale Moon',
perplexitybot: 'PerplexityBot',
perplexity_user: 'Perplexity-User',
phantomjs: 'PhantomJS', phantomjs: 'PhantomJS',
pingdombot: 'PingdomBot',
puffin: 'Puffin', puffin: 'Puffin',
qupzilla: 'QupZilla',
qq: 'QQ Browser', qq: 'QQ Browser',
qqlite: 'QQ Browser Lite', qqlite: 'QQ Browser Lite',
qupzilla: 'QupZilla',
roku: 'Roku',
safari: 'Safari', safari: 'Safari',
sailfish: 'Sailfish', sailfish: 'Sailfish',
samsung_internet: 'Samsung Internet for Android', samsung_internet: 'Samsung Internet for Android',
seamonkey: 'SeaMonkey', seamonkey: 'SeaMonkey',
sleipnir: 'Sleipnir', sleipnir: 'Sleipnir',
sogou: 'Sogou Browser',
swing: 'Swing', swing: 'Swing',
tizen: 'Tizen', tizen: 'Tizen',
uc: 'UC Browser', uc: 'UC Browser',
vivaldi: 'Vivaldi', vivaldi: 'Vivaldi',
webos: 'WebOS Browser', webos: 'WebOS Browser',
wechat: 'WeChat', wechat: 'WeChat',
yahooslurp: 'YahooSlurp',
yandex: 'Yandex Browser', yandex: 'Yandex Browser',
yandexbot: 'YandexBot',
youbot: 'YouBot',
}; };
export const PLATFORMS_MAP = { export const PLATFORMS_MAP = {
tablet: 'tablet',
mobile: 'mobile',
desktop: 'desktop',
tv: 'tv',
bot: 'bot', bot: 'bot',
desktop: 'desktop',
mobile: 'mobile',
tablet: 'tablet',
tv: 'tv',
}; };
export const OS_MAP = { export const OS_MAP = {
WindowsPhone: 'Windows Phone',
Windows: 'Windows',
MacOS: 'macOS',
iOS: 'iOS',
Android: 'Android', Android: 'Android',
WebOS: 'WebOS',
BlackBerry: 'BlackBerry',
Bada: 'Bada', Bada: 'Bada',
Tizen: 'Tizen', BlackBerry: 'BlackBerry',
Linux: 'Linux',
ChromeOS: 'Chrome OS', ChromeOS: 'Chrome OS',
HarmonyOS: 'HarmonyOS',
iOS: 'iOS',
Linux: 'Linux',
MacOS: 'macOS',
PlayStation4: 'PlayStation 4', PlayStation4: 'PlayStation 4',
Roku: 'Roku', Roku: 'Roku',
Tizen: 'Tizen',
WebOS: 'WebOS',
Windows: 'Windows',
WindowsPhone: 'Windows Phone',
}; };
export const ENGINE_MAP = { export const ENGINE_MAP = {
EdgeHTML: 'EdgeHTML',
Blink: 'Blink', Blink: 'Blink',
Trident: 'Trident', EdgeHTML: 'EdgeHTML',
Presto: 'Presto',
Gecko: 'Gecko', Gecko: 'Gecko',
Presto: 'Presto',
Trident: 'Trident',
WebKit: 'WebKit', WebKit: 'WebKit',
}; };

View File

@ -28,6 +28,227 @@ import Utils from './utils.js';
const commonVersionIdentifier = /version\/(\d+(\.?_?\d+)+)/i; const commonVersionIdentifier = /version\/(\d+(\.?_?\d+)+)/i;
const browsersList = [ const browsersList = [
/* GPTBot */
{
test: [/gptbot/i],
describe(ua) {
const browser = {
name: 'GPTBot',
};
const version = Utils.getFirstMatch(/gptbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* ChatGPT-User */
{
test: [/chatgpt-user/i],
describe(ua) {
const browser = {
name: 'ChatGPT-User',
};
const version = Utils.getFirstMatch(/chatgpt-user\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* OAI-SearchBot */
{
test: [/oai-searchbot/i],
describe(ua) {
const browser = {
name: 'OAI-SearchBot',
};
const version = Utils.getFirstMatch(/oai-searchbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* ClaudeBot */
{
test: [/claudebot/i, /claude-web/i, /claude-user/i, /claude-searchbot/i],
describe(ua) {
const browser = {
name: 'ClaudeBot',
};
const version = Utils.getFirstMatch(/(?:claudebot|claude-web|claude-user|claude-searchbot)\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* Omgilibot */
{
test: [/omgilibot/i, /webzio-extended/i],
describe(ua) {
const browser = {
name: 'Omgilibot',
};
const version = Utils.getFirstMatch(/(?:omgilibot|webzio-extended)\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* Diffbot */
{
test: [/diffbot/i],
describe(ua) {
const browser = {
name: 'Diffbot',
};
const version = Utils.getFirstMatch(/diffbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* PerplexityBot */
{
test: [/perplexitybot/i],
describe(ua) {
const browser = {
name: 'PerplexityBot',
};
const version = Utils.getFirstMatch(/perplexitybot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* Perplexity-User */
{
test: [/perplexity-user/i],
describe(ua) {
const browser = {
name: 'Perplexity-User',
};
const version = Utils.getFirstMatch(/perplexity-user\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* YouBot */
{
test: [/youbot/i],
describe(ua) {
const browser = {
name: 'YouBot',
};
const version = Utils.getFirstMatch(/youbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* Meta-WebIndexer */
{
test: [/meta-webindexer/i],
describe(ua) {
const browser = {
name: 'Meta-WebIndexer',
};
const version = Utils.getFirstMatch(/meta-webindexer\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* Meta-ExternalAds */
{
test: [/meta-externalads/i],
describe(ua) {
const browser = {
name: 'Meta-ExternalAds',
};
const version = Utils.getFirstMatch(/meta-externalads\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* Meta-ExternalAgent */
{
test: [/meta-externalagent/i],
describe(ua) {
const browser = {
name: 'Meta-ExternalAgent',
};
const version = Utils.getFirstMatch(/meta-externalagent\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* Meta-ExternalFetcher */
{
test: [/meta-externalfetcher/i],
describe(ua) {
const browser = {
name: 'Meta-ExternalFetcher',
};
const version = Utils.getFirstMatch(/meta-externalfetcher\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* Googlebot */ /* Googlebot */
{ {
test: [/googlebot/i], test: [/googlebot/i],
@ -45,6 +266,131 @@ const browsersList = [
}, },
}, },
/* AmazonBot */
{
test: [/amazonbot/i],
describe(ua) {
const browser = {
name: 'AmazonBot',
};
const version = Utils.getFirstMatch(/amazonbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* BingCrawler */
{
test: [/bingbot/i],
describe(ua) {
const browser = {
name: 'BingCrawler',
};
const version = Utils.getFirstMatch(/bingbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* BaiduSpider */
{
test: [/baiduspider/i],
describe(ua) {
const browser = {
name: 'BaiduSpider',
};
const version = Utils.getFirstMatch(/baiduspider\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* DuckDuckBot */
{
test: [/duckduckbot/i],
describe(ua) {
const browser = {
name: 'DuckDuckBot',
};
const version = Utils.getFirstMatch(/duckduckbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* InternetArchiveCrawler */
{
test: [/ia_archiver/i],
describe(ua) {
const browser = {
name: 'InternetArchiveCrawler',
};
const version = Utils.getFirstMatch(/ia_archiver\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
/* FacebookExternalHit */
{
test: [/facebookexternalhit/i, /facebookcatalog/i],
describe() {
return {
name: 'FacebookExternalHit',
};
},
},
/* YahooSlurp */
{
test: [/yahoo!?[\s/]*slurp/i],
describe() {
return {
name: 'YahooSlurp',
};
},
},
/* YandexBot */
{
test: [/yandexbot/i, /yandexmobilebot/i],
describe() {
return {
name: 'YandexBot',
};
},
},
/* PingdomBot */
{
test: [/pingdom/i],
describe() {
return {
name: 'PingdomBot',
};
},
},
/* Opera < 13.0 */ /* Opera < 13.0 */
{ {
test: [/opera/i], test: [/opera/i],
@ -546,6 +892,21 @@ const browsersList = [
return browser; return browser;
}, },
}, },
{
test: [/librewolf/i],
describe(ua) {
const browser = {
name: 'LibreWolf',
};
const version = Utils.getFirstMatch(/(?:librewolf)[\s/](\d+(\.?_?\d+)+)/i, ua);
if (version) {
browser.version = version;
}
return browser;
},
},
{ {
test: [/firefox|iceweasel|fxios/i], test: [/firefox|iceweasel|fxios/i],
describe(ua) { describe(ua) {
@ -576,6 +937,24 @@ const browsersList = [
return browser; return browser;
}, },
}, },
{
test: [/sogoumobilebrowser/i, /metasr/i, /se 2\.[x]/i],
describe(ua) {
const browser = {
name: 'Sogou Browser',
};
const sogouMobileVersion = Utils.getFirstMatch(/(?:sogoumobilebrowser)[\s/](\d+(\.?_?\d+)+)/i, ua);
const chromiumVersion = Utils.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua);
const seVersion = Utils.getFirstMatch(/se ([\d.]+)x/i, ua);
const version = sogouMobileVersion || chromiumVersion || seVersion;
if (version) {
browser.version = version;
}
return browser;
},
},
{ {
test: [/MiuiBrowser/i], test: [/MiuiBrowser/i],
describe(ua) { describe(ua) {

View File

@ -87,6 +87,18 @@ export default [
}, },
}, },
/* HarmonyOS */
{
test: [/OpenHarmony/i],
describe(ua) {
const version = Utils.getFirstMatch(/OpenHarmony\s+(\d+(\.\d+)*)/i, ua);
return {
name: OS_MAP.HarmonyOS,
version,
};
},
},
/* Android */ /* Android */
{ {
test(parser) { test(parser) {

View File

@ -18,6 +18,248 @@ export default [
}, },
}, },
/* AmazonBot */
{
test: [/amazonbot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Amazon',
};
},
},
/* GPTBot */
{
test: [/gptbot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'OpenAI',
};
},
},
/* ChatGPT-User */
{
test: [/chatgpt-user/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'OpenAI',
};
},
},
/* OAI-SearchBot */
{
test: [/oai-searchbot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'OpenAI',
};
},
},
/* Baidu */
{
test: [/baiduspider/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Baidu',
};
},
},
/* Bingbot */
{
test: [/bingbot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Bing',
};
},
},
/* DuckDuckBot */
{
test: [/duckduckbot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'DuckDuckGo',
};
},
},
/* ClaudeBot */
{
test: [/claudebot/i, /claude-web/i, /claude-user/i, /claude-searchbot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Anthropic',
};
},
},
/* Omgilibot */
{
test: [/omgilibot/i, /webzio-extended/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Webz.io',
};
},
},
/* Diffbot */
{
test: [/diffbot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Diffbot',
};
},
},
/* PerplexityBot */
{
test: [/perplexitybot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Perplexity AI',
};
},
},
/* Perplexity-User */
{
test: [/perplexity-user/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Perplexity AI',
};
},
},
/* YouBot */
{
test: [/youbot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'You.com',
};
},
},
/* Internet Archive Crawler */
{
test: [/ia_archiver/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Internet Archive',
};
},
},
/* Meta-WebIndexer */
{
test: [/meta-webindexer/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Meta',
};
},
},
/* Meta-ExternalAds */
{
test: [/meta-externalads/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Meta',
};
},
},
/* Meta-ExternalAgent */
{
test: [/meta-externalagent/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Meta',
};
},
},
/* Meta-ExternalFetcher */
{
test: [/meta-externalfetcher/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Meta',
};
},
},
/* Meta Web Crawler */
{
test: [/facebookexternalhit/i, /facebookcatalog/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Meta',
};
},
},
/* Yahoo! Slurp */
{
test: [/yahoo/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Yahoo',
};
},
},
/* Yandex */
{
test: [/yandexbot/i, /yandexmobilebot/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Yandex',
};
},
},
/* Pingdom */
{
test: [/pingdom/i],
describe() {
return {
type: PLATFORMS_MAP.bot,
vendor: 'Pingdom',
};
},
},
/* Huawei */ /* Huawei */
{ {
test: [/huawei/i], test: [/huawei/i],

View File

@ -66,6 +66,11 @@ export default class Utils {
* 10.13 - High Sierra * 10.13 - High Sierra
* 10.14 - Mojave * 10.14 - Mojave
* 10.15 - Catalina * 10.15 - Catalina
* 11 - Big Sur
* 12 - Monterey
* 13 - Ventura
* 14 - Sonoma
* 15 - Sequoia
* *
* @example * @example
* getMacOSVersionName("10.14") // 'Mojave' * getMacOSVersionName("10.14") // 'Mojave'
@ -76,8 +81,11 @@ export default class Utils {
static getMacOSVersionName(version) { static getMacOSVersionName(version) {
const v = version.split('.').splice(0, 2).map(s => parseInt(s, 10) || 0); const v = version.split('.').splice(0, 2).map(s => parseInt(s, 10) || 0);
v.push(0); v.push(0);
if (v[0] !== 10) return undefined; const major = v[0];
switch (v[1]) { const minor = v[1];
if (major === 10) {
switch (minor) {
case 5: return 'Leopard'; case 5: return 'Leopard';
case 6: return 'Snow Leopard'; case 6: return 'Snow Leopard';
case 7: return 'Lion'; case 7: return 'Lion';
@ -93,6 +101,16 @@ export default class Utils {
} }
} }
switch (major) {
case 11: return 'Big Sur';
case 12: return 'Monterey';
case 13: return 'Ventura';
case 14: return 'Sonoma';
case 15: return 'Sequoia';
default: return undefined;
}
}
/** /**
* Get Android version name * Get Android version name
* 1.5 - Cupcake * 1.5 - Cupcake

View File

@ -365,6 +365,20 @@
type: "mobile" type: "mobile"
engine: engine:
name: "Blink" name: "Blink"
-
ua: "Mozilla/5.0 (Phone; OpenHarmony 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile HuaweiBrowser/5.1.5.352"
spec:
browser:
name: "Chrome"
version: "114.0.0.0"
os:
name: "HarmonyOS"
version: "5.0"
platform:
type: "mobile"
vendor: "Huawei"
engine:
name: "Blink"
Google Search: Google Search:
- -
ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) GSA/83.0.268992909 Mobile/15E148 Safari/605.1" ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) GSA/83.0.268992909 Mobile/15E148 Safari/605.1"
@ -905,6 +919,86 @@
engine: engine:
name: "WebKit" name: "WebKit"
version: "525.28.3" version: "525.28.3"
-
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15"
spec:
browser:
name: "Safari"
version: "14.0"
os:
name: "macOS"
version: "11.0"
versionName: "Big Sur"
platform:
type: "desktop"
vendor: "Apple"
engine:
name: "WebKit"
version: "605.1.15"
-
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15"
spec:
browser:
name: "Safari"
version: "15.4"
os:
name: "macOS"
version: "12.3"
versionName: "Monterey"
platform:
type: "desktop"
vendor: "Apple"
engine:
name: "WebKit"
version: "605.1.15"
-
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15"
spec:
browser:
name: "Safari"
version: "16.3"
os:
name: "macOS"
version: "13.2.1"
versionName: "Ventura"
platform:
type: "desktop"
vendor: "Apple"
engine:
name: "WebKit"
version: "605.1.15"
-
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15"
spec:
browser:
name: "Safari"
version: "17.0"
os:
name: "macOS"
version: "14.0"
versionName: "Sonoma"
platform:
type: "desktop"
vendor: "Apple"
engine:
name: "WebKit"
version: "605.1.15"
-
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15"
spec:
browser:
name: "Safari"
version: "18.0"
os:
name: "macOS"
version: "15.0"
versionName: "Sequoia"
platform:
type: "desktop"
vendor: "Apple"
engine:
name: "WebKit"
version: "605.1.15"
- -
ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11B554a" ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11B554a"
spec: spec:
@ -1901,6 +1995,20 @@
engine: engine:
name: "WebKit" name: "WebKit"
version: "605.1.15" version: "605.1.15"
LibreWolf:
-
ua: "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 LibreWolf/124.0.2"
spec:
browser:
name: "LibreWolf"
version: "124.0.2"
os:
name: "Linux"
platform:
type: "desktop"
engine:
name: "Gecko"
version: "20100101"
SeaMonkey: SeaMonkey:
- -
ua: "Mozilla/5.0 (Windows NT 5.2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 SeaMonkey/2.7.1" ua: "Mozilla/5.0 (Windows NT 5.2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 SeaMonkey/2.7.1"
@ -2535,6 +2643,379 @@
vendor: "Google" vendor: "Google"
engine: engine:
name: "Blink" name: "Blink"
GPTBot:
-
ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.1; +https://openai.com/gptbot)"
spec:
browser:
name: "GPTBot"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "OpenAI"
engine:
name: "Blink"
ChatGPT-User:
-
ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ChatGPT-User/1.0; +https://openai.com/bot)"
spec:
browser:
name: "ChatGPT-User"
version: "1.0"
os: {}
platform:
type: "bot"
vendor: "OpenAI"
engine:
name: "Blink"
OAI-SearchBot:
-
ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot)"
spec:
browser:
name: "OAI-SearchBot"
version: "1.0"
os: {}
platform:
type: "bot"
vendor: "OpenAI"
engine:
name: "Blink"
ClaudeBot:
-
ua: "Mozilla/5.0 (compatible; ClaudeBot/1.0; +https://www.anthropic.com/claudebot)"
spec:
browser:
name: "ClaudeBot"
version: "1.0"
os: {}
platform:
type: "bot"
vendor: "Anthropic"
engine: {}
Omgilibot:
-
ua: "Mozilla/5.0 (compatible; Omgilibot/1.0; +https://www.omgili.com)"
spec:
browser:
name: "Omgilibot"
version: "1.0"
os: {}
platform:
type: "bot"
vendor: "Webz.io"
engine: {}
Meta-WebIndexer:
-
ua: "meta-webindexer/1.1"
spec:
browser:
name: "Meta-WebIndexer"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
-
ua: "meta-webindexer/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)"
spec:
browser:
name: "Meta-WebIndexer"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
Meta-ExternalAds:
-
ua: "meta-externalads/1.1"
spec:
browser:
name: "Meta-ExternalAds"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
-
ua: "meta-externalads/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)"
spec:
browser:
name: "Meta-ExternalAds"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
Meta-ExternalAgent:
-
ua: "meta-externalagent/1.1"
spec:
browser:
name: "Meta-ExternalAgent"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
-
ua: "meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)"
spec:
browser:
name: "Meta-ExternalAgent"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
Meta-ExternalFetcher:
-
ua: "meta-externalfetcher/1.1"
spec:
browser:
name: "Meta-ExternalFetcher"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
-
ua: "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)"
spec:
browser:
name: "Meta-ExternalFetcher"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
Diffbot:
-
ua: "Mozilla/5.0 (compatible; Diffbot/3.0; +http://www.diffbot.com)"
spec:
browser:
name: "Diffbot"
version: "3.0"
os: {}
platform:
type: "bot"
vendor: "Diffbot"
engine: {}
PerplexityBot:
-
ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)"
spec:
browser:
name: "PerplexityBot"
version: "1.0"
os: {}
platform:
type: "bot"
vendor: "Perplexity AI"
engine:
name: "Blink"
Perplexity-User:
-
ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Perplexity-User/1.0; +https://perplexity.ai/perplexity-user)"
spec:
browser:
name: "Perplexity-User"
version: "1.0"
os: {}
platform:
type: "bot"
vendor: "Perplexity AI"
engine:
name: "Blink"
YouBot:
-
ua: "Mozilla/5.0 (compatible; YouBot/1.0; +https://you.com/bot)"
spec:
browser:
name: "YouBot"
version: "1.0"
os: {}
platform:
type: "bot"
vendor: "You.com"
engine: {}
AmazonBot:
-
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)"
spec:
browser:
name: "AmazonBot"
version: "0.1"
os:
name: "macOS"
version: "10.10.1"
versionName: "Yosemite"
platform:
type: "bot"
vendor: "Amazon"
engine:
name: "WebKit"
version: "600.2.5"
BingCrawler:
-
ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/"
spec:
browser:
name: "BingCrawler"
version: "2.0"
os: {}
platform:
type: "bot"
vendor: "Bing"
engine:
name: "Blink"
-
ua: "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) 80.0.345.0 Safari/537.36"
spec:
browser:
name: "BingCrawler"
version: "2.0"
os: {}
platform:
type: "bot"
vendor: "Bing"
engine: {}
-
ua: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.345.0Mobile Safari/537.36 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"
spec:
browser:
name: "BingCrawler"
version: "2.0"
os:
name: "Android"
version: "6.0.1"
versionName: "Marshmallow"
platform:
type: "bot"
vendor: "Bing"
engine:
name: "Blink"
BaiduSpider:
-
ua: "Baiduspider"
spec:
browser:
name: "BaiduSpider"
os: {}
platform:
type: "bot"
vendor: "Baidu"
engine: {}
DuckDuckBot:
-
ua: "DuckDuckBot/1.1; (+http://duckduckgo.com/duckduckbot.html)"
spec:
browser:
name: "DuckDuckBot"
version: "1.1"
os: {}
platform:
type: "bot"
vendor: "DuckDuckGo"
engine: {}
InternetArchiveCrawler:
-
ua: "ia_archiver"
spec:
browser:
name: "InternetArchiveCrawler"
os: {}
platform:
type: "bot"
vendor: "Internet Archive"
engine: {}
FacebookExternalHit:
-
ua: "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)"
spec:
browser:
name: "FacebookExternalHit"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
-
ua: "facebookexternalhit/1.1"
spec:
browser:
name: "FacebookExternalHit"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
-
ua: "facebookcatalog/1.0"
spec:
browser:
name: "FacebookExternalHit"
os: {}
platform:
type: "bot"
vendor: "Meta"
engine: {}
YahooSlurp:
-
ua: "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)"
spec:
browser:
name: "YahooSlurp"
os: {}
platform:
type: "bot"
vendor: "Yahoo"
engine: {}
YandexBot:
-
ua: "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)"
spec:
browser:
name: "YandexBot"
os: {}
platform:
type: "bot"
vendor: "Yandex"
engine: {}
-
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)"
spec:
browser:
name: "YandexBot"
os:
name: "iOS"
version: "8.1"
platform:
type: "bot"
vendor: "Yandex"
engine:
name: "WebKit"
version: "600.1.4"
PingdomBot:
-
ua: "Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)"
spec:
browser:
name: "PingdomBot"
os: {}
platform:
type: "bot"
vendor: "Pingdom"
engine: {}
WeChat: WeChat:
- -
ua: "Mozilla/5.0 (iPad; U; CPU OS 9 like Mac OS X; en-us; iPad4,4) AppleWebKit/534.46 (KHTML, like Gecko) MicroMessenger/6.5.2.501 U3/1 Safari/7543.48.3" ua: "Mozilla/5.0 (iPad; U; CPU OS 9 like Mac OS X; en-us; iPad4,4) AppleWebKit/534.46 (KHTML, like Gecko) MicroMessenger/6.5.2.501 U3/1 Safari/7543.48.3"
@ -2822,6 +3303,51 @@
type: "mobile" type: "mobile"
engine: engine:
name: "Blink" name: "Blink"
Sogou Browser:
-
ua: "Mozilla/5.0 (Linux; Android 5.1.1; SM-G9280 Build/LMY47X; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile Safari/537.36 SogouMobileBrowser/3.5.3"
spec:
browser:
name: "Sogou Browser"
version: "3.5.3"
os:
name: "Android"
version: "5.1.1"
versionName: "Lollipop"
platform:
type: "mobile"
engine:
name: "Blink"
-
ua: "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0"
spec:
browser:
name: "Sogou Browser"
version: "49.0.2623.221"
os:
name: "Windows"
version: "NT 6.1"
versionName: "7"
platform:
type: "desktop"
engine:
name: "Blink"
-
ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 SogouMobileBrowser/5.22.1"
spec:
browser:
name: "Sogou Browser"
version: "5.22.1"
os:
name: "iOS"
version: "12.4.8"
platform:
type: "mobile"
vendor: "Apple"
model: "iPhone"
engine:
name: "WebKit"
version: "605.1.15"
NAVER Whale Browser: NAVER Whale Browser:
- -
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Whale/1.0.40.10 Safari/537.36" ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Whale/1.0.40.10 Safari/537.36"

View File

@ -38,6 +38,11 @@ test('getWindowsVersionName', (t) => {
test('getMacOSVersionName', (t) => { test('getMacOSVersionName', (t) => {
t.is(getMacOSVersionName('10.14.5'), 'Mojave'); t.is(getMacOSVersionName('10.14.5'), 'Mojave');
t.is(getMacOSVersionName('10.15'), 'Catalina'); t.is(getMacOSVersionName('10.15'), 'Catalina');
t.is(getMacOSVersionName('11.0'), 'Big Sur');
t.is(getMacOSVersionName('12.3.1'), 'Monterey');
t.is(getMacOSVersionName('13.2.1'), 'Ventura');
t.is(getMacOSVersionName('14.0'), 'Sonoma');
t.is(getMacOSVersionName('15.1'), 'Sequoia');
t.is(getMacOSVersionName('10.999999'), void 0); t.is(getMacOSVersionName('10.999999'), void 0);
t.is(getMacOSVersionName('XXX'), void 0); t.is(getMacOSVersionName('XXX'), void 0);
}); });