mirror of
https://github.com/faisalman/ua-parser-js.git
synced 2025-09-27 07:58:45 +03:00
[helpers] split helpers
into 2 new packages: user-agent-helpers
& client-hints-helpers
This commit is contained in:
parent
129657673b
commit
3f105fe93b
24
package-lock.json
generated
24
package-lock.json
generated
@ -23,7 +23,8 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"src/helpers"
|
||||
"src/client-hints-helpers",
|
||||
"src/user-agent-helpers"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/parser": "7.15.8",
|
||||
@ -763,8 +764,12 @@
|
||||
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@ua-parser-js/helpers": {
|
||||
"resolved": "src/helpers",
|
||||
"node_modules/@ua-parser-js/client-hints-helpers": {
|
||||
"resolved": "src/client-hints-helpers",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@ua-parser-js/user-agent-helpers": {
|
||||
"resolved": "src/user-agent-helpers",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@ungap/promise-all-settled": {
|
||||
@ -3778,9 +3783,20 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"src/client-hints-helpers": {
|
||||
"name": "@ua-parser-js/client-hints-helpers",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"src/helpers": {
|
||||
"name": "@ua-parser-js/helpers",
|
||||
"version": "2.0.0-alpha.3",
|
||||
"version": "0.0.3",
|
||||
"extraneous": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"src/user-agent-helpers": {
|
||||
"name": "@ua-parser-js/user-agent-helpers",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,64 @@
|
||||
#!/usr/bin/env node
|
||||
/* jshint esversion: 6 */
|
||||
const fs = require('fs');
|
||||
const PATH = {
|
||||
main : {
|
||||
src : 'src/main/ua-parser.js',
|
||||
dest : 'src/main/ua-parser.mjs',
|
||||
title : ''
|
||||
},
|
||||
enums : {
|
||||
src : 'src/enums/ua-parser-enums.js',
|
||||
dest :'src/enums/ua-parser-enums.mjs',
|
||||
title : 'enums'
|
||||
},
|
||||
extensions : {
|
||||
src : 'src/extensions/ua-parser-extensions.js',
|
||||
dest : 'src/extensions/ua-parser-extensions.mjs',
|
||||
title : 'extensions'
|
||||
},
|
||||
helpers : {
|
||||
src : 'src/helpers/ua-parser-helpers.js',
|
||||
dest : 'src/helpers/ua-parser-helpers.mjs',
|
||||
title : 'helpers'
|
||||
}
|
||||
};
|
||||
const generateMJS = (module, replacers) => {
|
||||
const { src, dest, title } = PATH[module];
|
||||
|
||||
const generateMJS = (module) => {
|
||||
let { src, dest, title, replacements } = module;
|
||||
let text = fs.readFileSync(src, 'utf-8');
|
||||
replacers.forEach(replacer => {
|
||||
text = text.replace(replacer[0], replacer[1]);
|
||||
|
||||
replacements.push(
|
||||
[/const (.+?)\s*=\s*require\((.+)\)/ig, 'import $1 from $2'],
|
||||
[/module\.exports =/ig, 'export']
|
||||
);
|
||||
replacements.forEach(rep => {
|
||||
text = text.replace(rep[0], rep[1]);
|
||||
});
|
||||
|
||||
console.log(`Generate ${dest}`);
|
||||
|
||||
fs.writeFileSync(dest,
|
||||
`// Generated ESM version of UAParser.js ${title}
|
||||
`// Generated ESM version of ${title}
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /${src}
|
||||
|
||||
${text}`, 'utf-8');
|
||||
|
||||
};
|
||||
|
||||
// ua-parser.mjs
|
||||
generateMJS('main', [
|
||||
const modules = [
|
||||
{
|
||||
src : 'src/main/ua-parser.js',
|
||||
dest : 'src/main/ua-parser.mjs',
|
||||
title : 'ua-parser-js',
|
||||
replacements : [
|
||||
[/\(func[\s\S]+strict\';/ig, ''],
|
||||
[/esversion\: 3/ig, 'esversion: 6'],
|
||||
[/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};']
|
||||
]);
|
||||
]
|
||||
},{
|
||||
src : 'src/enums/ua-parser-enums.js',
|
||||
dest :'src/enums/ua-parser-enums.mjs',
|
||||
title : 'ua-parser-js/enums',
|
||||
replacements : []
|
||||
},
|
||||
{
|
||||
src : 'src/extensions/ua-parser-extensions.js',
|
||||
dest : 'src/extensions/ua-parser-extensions.mjs',
|
||||
title : 'ua-parser-js/extensions',
|
||||
replacements : []
|
||||
},
|
||||
{
|
||||
src : 'src/user-agent-helpers/user-agent-helpers.js',
|
||||
dest : 'src/user-agent-helpers/user-agent-helpers.mjs',
|
||||
title : '@ua-parser-js/user-agent-helpers',
|
||||
replacements : []
|
||||
},
|
||||
{
|
||||
src : 'src/client-hints-helpers/client-hints-helpers.js',
|
||||
dest : 'src/client-hints-helpers/client-hints-helpers.mjs',
|
||||
title : '@ua-parser-js/client-hints-helpers',
|
||||
replacements : []
|
||||
}
|
||||
];
|
||||
|
||||
// ua-parser-enum.mjs
|
||||
generateMJS('enums', [[/module\.exports =/ig, 'export']]);
|
||||
|
||||
// ua-parser-extension.mjs
|
||||
generateMJS('extensions', [[/module\.exports =/ig, 'export']]);
|
||||
|
||||
// ua-parser-helpers.mjs
|
||||
generateMJS('helpers', [[/module\.exports =/ig, 'export']]);
|
||||
modules.forEach(module => generateMJS(module));
|
@ -32,6 +32,4 @@ export interface ClientHintsHTTPHeaders {
|
||||
'sec-ch-ua-wow64'?: string;
|
||||
}
|
||||
|
||||
export function isFrozenUA(ua: string): boolean;
|
||||
export function unfreezeUA(): Promise<string>;
|
||||
export function UACHParser(headers: ClientHintsHTTPHeaders): ClientHintsJSHighEntropy;
|
88
src/client-hints-helpers/client-hints-helpers.js
Normal file
88
src/client-hints-helpers/client-hints-helpers.js
Normal file
@ -0,0 +1,88 @@
|
||||
////////////////////////////////////////////////////
|
||||
/* A collection of utility methods for client-hints
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 11 */
|
||||
|
||||
const UACHMap = {
|
||||
'sec-ch-ua-arch' : {
|
||||
prop : 'architecture',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-bitness' : {
|
||||
prop : 'bitness',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua' : {
|
||||
prop : 'brands',
|
||||
type : 'sf-list'
|
||||
},
|
||||
'sec-ch-ua-form-factor' : {
|
||||
prop : 'formFactor',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-full-version-list' : {
|
||||
prop : 'fullVersionList',
|
||||
type : 'sf-list'
|
||||
},
|
||||
'sec-ch-ua-mobile' : {
|
||||
prop : 'mobile',
|
||||
type : 'sf-boolean',
|
||||
},
|
||||
'sec-ch-ua-model' : {
|
||||
prop : 'model',
|
||||
type : 'sf-string',
|
||||
},
|
||||
'sec-ch-ua-platform' : {
|
||||
prop : 'platform',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-platform-version' : {
|
||||
prop : 'platformVersion',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-wow64' : {
|
||||
prop : 'wow64',
|
||||
type : 'sf-boolean'
|
||||
}
|
||||
};
|
||||
|
||||
const UACHParser = (headers) => {
|
||||
const parse = (str, type) => {
|
||||
if (!str) {
|
||||
return '';
|
||||
}
|
||||
switch (type) {
|
||||
case 'sf-boolean':
|
||||
return /\?1/.test(str);
|
||||
case 'sf-list':
|
||||
return str.replace(/\\?\"/g, '')
|
||||
.split(', ')
|
||||
.map(brands => {
|
||||
const [brand, version] = brands.split(';v=');
|
||||
return {
|
||||
brand : brand,
|
||||
version : version
|
||||
};
|
||||
});
|
||||
case 'sf-string':
|
||||
default:
|
||||
return str.replace(/\\?\"/g, '');
|
||||
}
|
||||
};
|
||||
let ch = {};
|
||||
Object.keys(UACHMap).forEach(field => {
|
||||
if (headers.hasOwnProperty(field)) {
|
||||
const { prop, type } = UACHMap[field];
|
||||
ch[prop] = parse(headers[field], type);
|
||||
}
|
||||
});
|
||||
return ch;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
UACHParser
|
||||
};
|
92
src/client-hints-helpers/client-hints-helpers.mjs
Normal file
92
src/client-hints-helpers/client-hints-helpers.mjs
Normal file
@ -0,0 +1,92 @@
|
||||
// Generated ESM version of @ua-parser-js/client-hints-helpers
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/client-hints-helpers/client-hints-helpers.js
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
/* A collection of utility methods for client-hints
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 11 */
|
||||
|
||||
const UACHMap = {
|
||||
'sec-ch-ua-arch' : {
|
||||
prop : 'architecture',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-bitness' : {
|
||||
prop : 'bitness',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua' : {
|
||||
prop : 'brands',
|
||||
type : 'sf-list'
|
||||
},
|
||||
'sec-ch-ua-form-factor' : {
|
||||
prop : 'formFactor',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-full-version-list' : {
|
||||
prop : 'fullVersionList',
|
||||
type : 'sf-list'
|
||||
},
|
||||
'sec-ch-ua-mobile' : {
|
||||
prop : 'mobile',
|
||||
type : 'sf-boolean',
|
||||
},
|
||||
'sec-ch-ua-model' : {
|
||||
prop : 'model',
|
||||
type : 'sf-string',
|
||||
},
|
||||
'sec-ch-ua-platform' : {
|
||||
prop : 'platform',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-platform-version' : {
|
||||
prop : 'platformVersion',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-wow64' : {
|
||||
prop : 'wow64',
|
||||
type : 'sf-boolean'
|
||||
}
|
||||
};
|
||||
|
||||
const UACHParser = (headers) => {
|
||||
const parse = (str, type) => {
|
||||
if (!str) {
|
||||
return '';
|
||||
}
|
||||
switch (type) {
|
||||
case 'sf-boolean':
|
||||
return /\?1/.test(str);
|
||||
case 'sf-list':
|
||||
return str.replace(/\\?\"/g, '')
|
||||
.split(', ')
|
||||
.map(brands => {
|
||||
const [brand, version] = brands.split(';v=');
|
||||
return {
|
||||
brand : brand,
|
||||
version : version
|
||||
};
|
||||
});
|
||||
case 'sf-string':
|
||||
default:
|
||||
return str.replace(/\\?\"/g, '');
|
||||
}
|
||||
};
|
||||
let ch = {};
|
||||
Object.keys(UACHMap).forEach(field => {
|
||||
if (headers.hasOwnProperty(field)) {
|
||||
const { prop, type } = UACHMap[field];
|
||||
ch[prop] = parse(headers[field], type);
|
||||
}
|
||||
});
|
||||
return ch;
|
||||
};
|
||||
|
||||
export {
|
||||
UACHParser
|
||||
};
|
@ -1,13 +1,13 @@
|
||||
{
|
||||
"title": "UAParser.js Helpers",
|
||||
"name": "@ua-parser-js/helpers",
|
||||
"version": "0.0.3",
|
||||
"title": "Client-Hints Helpers",
|
||||
"name": "@ua-parser-js/client-hints-helpers",
|
||||
"version": "0.0.1",
|
||||
"author": "Faisal Salman <f@faisalman.com>",
|
||||
"description": "A collection of utility methods for UAParser.js",
|
||||
"main": "ua-parser-helpers.js",
|
||||
"module": "ua-parser-helpers.mjs",
|
||||
"description": "A collection of utility methods for working with client-hints",
|
||||
"main": "client-hints-helpers.js",
|
||||
"module": "client-hints-helpers.mjs",
|
||||
"scripts": {
|
||||
"test": "echo 1"
|
||||
"test": "mocha ../../test/mocha-test-helpers"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
75
src/client-hints-helpers/readme.md
Normal file
75
src/client-hints-helpers/readme.md
Normal file
@ -0,0 +1,75 @@
|
||||
# @ua-parser-js/client-hints-helpers
|
||||
|
||||
This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client-hints.
|
||||
|
||||
```sh
|
||||
npm i @ua-parser-js/client-hints-helpers
|
||||
```
|
||||
|
||||
### * `UACHParser(headers:object):object`
|
||||
|
||||
Parse user-agent client-hints HTTP headers (sec-ch-ua) into its JS API equivalent
|
||||
|
||||
## Code Example
|
||||
|
||||
```js
|
||||
import { UACHParser } from '@ua-parser-js/client-hints-helpers';
|
||||
|
||||
/*
|
||||
Suppose we're in a server having this client hints data:
|
||||
|
||||
const headers = {
|
||||
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
|
||||
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
|
||||
'sec-ch-ua-arch' : 'arm',
|
||||
'sec-ch-ua-bitness' : '64',
|
||||
'sec-ch-ua-mobile' : '?1',
|
||||
'sec-ch-ua-model' : 'Pixel 99',
|
||||
'sec-ch-ua-platform' : 'Linux',
|
||||
'sec-ch-ua-platform-version' : '13',
|
||||
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
|
||||
};
|
||||
*/
|
||||
|
||||
const userAgentData = UACHParser(headers);
|
||||
|
||||
console.log(userAgentData);
|
||||
/*
|
||||
{
|
||||
"architecture": "arm",
|
||||
"bitness": "64",
|
||||
"brands": [
|
||||
{
|
||||
"brand": "Chromium",
|
||||
"version": "93"
|
||||
},
|
||||
{
|
||||
"brand": "Google Chrome",
|
||||
"version": "93"
|
||||
},
|
||||
{
|
||||
"brand": " Not;A Brand",
|
||||
"version": "99"
|
||||
}
|
||||
],
|
||||
"fullVersionList": [
|
||||
{
|
||||
"brand": "Chromium",
|
||||
"version": "93.0.1.2"
|
||||
},
|
||||
{
|
||||
"brand": "Google Chrome",
|
||||
"version": "93.0.1.2"
|
||||
},
|
||||
{
|
||||
"brand": " Not;A Brand",
|
||||
"version": "99.0.1.2"
|
||||
}
|
||||
],
|
||||
"mobile": true,
|
||||
"model": "Pixel 99",
|
||||
"platform": "Linux",
|
||||
"platformVersion": "13"
|
||||
}
|
||||
*/
|
||||
```
|
@ -1,131 +0,0 @@
|
||||
# @ua-parser-js/helpers
|
||||
|
||||
This package contains a collection of utility methods for [UAParser.js](https://github.com/faisalman/ua-parser-js)
|
||||
|
||||
```sh
|
||||
npm i @ua-parser-js/helpers
|
||||
```
|
||||
|
||||
### * `isFrozenUA(ua:string):boolean`
|
||||
|
||||
Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/)
|
||||
|
||||
### * `unfreezeUA():Promise<string>`
|
||||
|
||||
Construct new unfreezed user-agent string using real data from client hints
|
||||
|
||||
### * `UACHParser(headers: object): object`
|
||||
|
||||
Parse client hints HTTP headers (sec-ch-ua) into its JS API equivalent
|
||||
|
||||
## Code Example
|
||||
|
||||
```js
|
||||
import { isFrozenUA } from '@ua-parser-js/helpers';
|
||||
|
||||
const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
|
||||
const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
|
||||
|
||||
console.log(isFrozenUA(regularMobileUA));
|
||||
// false
|
||||
|
||||
console.log(isFrozenUA(freezedMobileUA));
|
||||
// true
|
||||
```
|
||||
|
||||
```js
|
||||
import { unfreezeUA } from '@ua-parser-js/helpers';
|
||||
|
||||
/*
|
||||
Suppose we're in a browser having this client hints data:
|
||||
|
||||
{
|
||||
fullVersionList: [
|
||||
{
|
||||
brand: 'New Browser',
|
||||
version: '110.1.2.3'
|
||||
},
|
||||
{
|
||||
brand: 'Chromium',
|
||||
version: '110.1.2.3'
|
||||
},
|
||||
{
|
||||
brand: 'Not(A:Brand',
|
||||
version: '110'
|
||||
}
|
||||
],
|
||||
platform: 'Windows',
|
||||
platformVersion: '13.0.0'
|
||||
}
|
||||
|
||||
And a freezed user-agent:
|
||||
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
|
||||
*/
|
||||
|
||||
unfreezeUA()
|
||||
.then(ua => console.log(ua));
|
||||
// 'Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'
|
||||
```
|
||||
|
||||
```js
|
||||
import { UACHParser } from '@ua-parser-js/helpers';
|
||||
|
||||
/*
|
||||
Suppose we're in a server having this client hints data:
|
||||
|
||||
const headers = {
|
||||
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
|
||||
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
|
||||
'sec-ch-ua-arch' : 'arm',
|
||||
'sec-ch-ua-bitness' : '64',
|
||||
'sec-ch-ua-mobile' : '?1',
|
||||
'sec-ch-ua-model' : 'Pixel 99',
|
||||
'sec-ch-ua-platform' : 'Linux',
|
||||
'sec-ch-ua-platform-version' : '13',
|
||||
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
|
||||
};
|
||||
*/
|
||||
|
||||
const userAgentData = UACHParser(headers);
|
||||
|
||||
console.log(userAgentData);
|
||||
/*
|
||||
{
|
||||
"architecture": "arm",
|
||||
"bitness": "64",
|
||||
"brands": [
|
||||
{
|
||||
"brand": "Chromium",
|
||||
"version": "93"
|
||||
},
|
||||
{
|
||||
"brand": "Google Chrome",
|
||||
"version": "93"
|
||||
},
|
||||
{
|
||||
"brand": " Not;A Brand",
|
||||
"version": "99"
|
||||
}
|
||||
],
|
||||
"fullVersionList": [
|
||||
{
|
||||
"brand": "Chromium",
|
||||
"version": "93.0.1.2"
|
||||
},
|
||||
{
|
||||
"brand": "Google Chrome",
|
||||
"version": "93.0.1.2"
|
||||
},
|
||||
{
|
||||
"brand": " Not;A Brand",
|
||||
"version": "99.0.1.2"
|
||||
}
|
||||
],
|
||||
"mobile": true,
|
||||
"model": "Pixel 99",
|
||||
"platform": "Linux",
|
||||
"platformVersion": "13"
|
||||
}
|
||||
*/
|
||||
```
|
@ -1,44 +0,0 @@
|
||||
// Generated ESM version of UAParser.js helpers
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/helpers/ua-parser-helpers.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Helpers for UAParser.js
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
/*
|
||||
# Reference:
|
||||
https://www.chromium.org/updates/ua-reduction/
|
||||
|
||||
# Desktop
|
||||
---
|
||||
Format:
|
||||
Mozilla/5.0 (<unifiedPlatform>) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<majorVersion>.0.0.0 Safari/537.36
|
||||
|
||||
Possible <unifiedPlatform> values:
|
||||
- Windows NT 10.0; Win64; x64
|
||||
- Macintosh; Intel Mac OS X 10_15_7
|
||||
- X11; Linux x86_64
|
||||
- X11; CrOS x86_64 14541.0.0
|
||||
- Fuchsia
|
||||
|
||||
# Mobile & Tablet: (except iOS/Android WebView)
|
||||
---
|
||||
Format:
|
||||
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<majorVersion>.0.0.0 <deviceCompat> Safari/537.36
|
||||
|
||||
Possible <deviceCompat> values:
|
||||
- "Mobile"
|
||||
- "" (empty string for Tablets & Desktop)
|
||||
*/
|
||||
|
||||
const isFrozenUA = ua => /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/.test(ua);
|
||||
|
||||
export {
|
||||
isFrozenUA
|
||||
};
|
32
src/user-agent-helpers/package.json
Normal file
32
src/user-agent-helpers/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"title": "User-Agent Helpers",
|
||||
"name": "@ua-parser-js/user-agent-helpers",
|
||||
"version": "0.0.1",
|
||||
"author": "Faisal Salman <f@faisalman.com>",
|
||||
"description": "A collection of utility methods for working with user-agent",
|
||||
"main": "user-agent-helpers.js",
|
||||
"module": "user-agent-helpers.mjs",
|
||||
"scripts": {
|
||||
"test": "mocha ../../test/mocha-test-helpers"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/faisalman/ua-parser-js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"ua-parser-js",
|
||||
"browser-detection",
|
||||
"device-detection",
|
||||
"os-detection",
|
||||
"user-agent",
|
||||
"client-hints"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/faisalman/ua-parser-js/issues"
|
||||
},
|
||||
"homepage": "https://github.com/faisalman/ua-parser-js#readme",
|
||||
"dependencies": {
|
||||
"@ua-parser-js/client-hints-helpers": "*"
|
||||
}
|
||||
}
|
67
src/user-agent-helpers/readme.md
Normal file
67
src/user-agent-helpers/readme.md
Normal file
@ -0,0 +1,67 @@
|
||||
# @ua-parser-js/user-agent-helpers
|
||||
|
||||
This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent.
|
||||
|
||||
```sh
|
||||
npm i @ua-parser-js/user-agent-helpers
|
||||
```
|
||||
|
||||
### * `isFrozenUA(ua:string):boolean`
|
||||
|
||||
Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/)
|
||||
|
||||
### * `unfreezeUA([ua:string,ch:object]|[headers:object]):Promise<string>`
|
||||
|
||||
Construct new unfreezed user-agent string using real data from client hints
|
||||
|
||||
## Code Example
|
||||
|
||||
```js
|
||||
import { isFrozenUA } from '@ua-parser-js/user-agent-helpers';
|
||||
|
||||
const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
|
||||
const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
|
||||
|
||||
console.log(isFrozenUA(regularMobileUA));
|
||||
// false
|
||||
|
||||
console.log(isFrozenUA(freezedMobileUA));
|
||||
// true
|
||||
```
|
||||
|
||||
```js
|
||||
import { unfreezeUA } from '@ua-parser-js/user-agent-helpers';
|
||||
|
||||
/*
|
||||
Suppose we're in a browser having this client hints data:
|
||||
|
||||
{
|
||||
fullVersionList: [
|
||||
{
|
||||
brand: 'New Browser',
|
||||
version: '110.1.2.3'
|
||||
},
|
||||
{
|
||||
brand: 'Chromium',
|
||||
version: '110.1.2.3'
|
||||
},
|
||||
{
|
||||
brand: 'Not(A:Brand',
|
||||
version: '110'
|
||||
}
|
||||
],
|
||||
platform: 'Windows',
|
||||
platformVersion: '13.0.0',
|
||||
architecture: 'arm'
|
||||
}
|
||||
|
||||
With a frozen user-agent:
|
||||
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
|
||||
*/
|
||||
|
||||
// Now let's generate a complete user-agent:
|
||||
unfreezeUA()
|
||||
.then(newUA => console.log(newUA));
|
||||
// 'Mozilla/5.0 (Windows NT 11.0; ARM) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36'
|
||||
```
|
4
src/user-agent-helpers/user-agent-helpers.d.ts
vendored
Normal file
4
src/user-agent-helpers/user-agent-helpers.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export function isFrozenUA(ua: string): boolean;
|
||||
export function unfreezeUA(): Promise<string>;
|
||||
export function unfreezeUA(ua: string, ch: ClientHintsJSHighEntropy): Promise<string>;
|
||||
export function unfreezeUA(headers: ClientHintsHTTPHeaders): Promise<string>;
|
@ -1,12 +1,14 @@
|
||||
///////////////////////////////////////////////
|
||||
/* A collection of utility methods for UAParser.js
|
||||
////////////////////////////////////////////////////
|
||||
/* A collection of utility methods for user-agent
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
//////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 11 */
|
||||
|
||||
const { UACHParser } = require('@ua-parser-js/client-hints-helpers');
|
||||
|
||||
/*
|
||||
# Reference:
|
||||
https://www.chromium.org/updates/ua-reduction/
|
||||
@ -89,84 +91,7 @@ const unfreezeUA = async (ua, ch) => {
|
||||
return ua;
|
||||
};
|
||||
|
||||
const UACHMap = {
|
||||
'sec-ch-ua-arch' : {
|
||||
prop : 'architecture',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-bitness' : {
|
||||
prop : 'bitness',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua' : {
|
||||
prop : 'brands',
|
||||
type : 'sf-list'
|
||||
},
|
||||
'sec-ch-ua-form-factor' : {
|
||||
prop : 'formFactor',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-full-version-list' : {
|
||||
prop : 'fullVersionList',
|
||||
type : 'sf-list'
|
||||
},
|
||||
'sec-ch-ua-mobile' : {
|
||||
prop : 'mobile',
|
||||
type : 'sf-boolean',
|
||||
},
|
||||
'sec-ch-ua-model' : {
|
||||
prop : 'model',
|
||||
type : 'sf-string',
|
||||
},
|
||||
'sec-ch-ua-platform' : {
|
||||
prop : 'platform',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-platform-version' : {
|
||||
prop : 'platformVersion',
|
||||
type : 'sf-string'
|
||||
},
|
||||
'sec-ch-ua-wow64' : {
|
||||
prop : 'wow64',
|
||||
type : 'sf-boolean'
|
||||
}
|
||||
};
|
||||
|
||||
const UACHParser = (headers) => {
|
||||
const parse = (str, type) => {
|
||||
if (!str) {
|
||||
return '';
|
||||
}
|
||||
switch (type) {
|
||||
case 'sf-boolean':
|
||||
return /\?1/.test(str);
|
||||
case 'sf-list':
|
||||
return str.replace(/\\?\"/g, '')
|
||||
.split(', ')
|
||||
.map(brands => {
|
||||
const [brand, version] = brands.split(';v=');
|
||||
return {
|
||||
brand : brand,
|
||||
version : version
|
||||
};
|
||||
});
|
||||
case 'sf-string':
|
||||
default:
|
||||
return str.replace(/\\?\"/g, '');
|
||||
}
|
||||
};
|
||||
let ch = {};
|
||||
Object.keys(UACHMap).forEach(field => {
|
||||
if (headers.hasOwnProperty(field)) {
|
||||
const { prop, type } = UACHMap[field];
|
||||
ch[prop] = parse(headers[field], type);
|
||||
}
|
||||
});
|
||||
return ch;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
isFrozenUA,
|
||||
unfreezeUA,
|
||||
UACHParser
|
||||
unfreezeUA
|
||||
};
|
101
src/user-agent-helpers/user-agent-helpers.mjs
Normal file
101
src/user-agent-helpers/user-agent-helpers.mjs
Normal file
@ -0,0 +1,101 @@
|
||||
// Generated ESM version of @ua-parser-js/user-agent-helpers
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/user-agent-helpers/user-agent-helpers.js
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
/* A collection of utility methods for user-agent
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 11 */
|
||||
|
||||
import { UACHParser } from '@ua-parser-js/client-hints-helpers';
|
||||
|
||||
/*
|
||||
# Reference:
|
||||
https://www.chromium.org/updates/ua-reduction/
|
||||
|
||||
# Desktop
|
||||
---
|
||||
Format:
|
||||
Mozilla/5.0 (<unifiedPlatform>) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<majorVersion>.0.0.0 Safari/537.36
|
||||
|
||||
Possible <unifiedPlatform> values:
|
||||
- Windows NT 10.0; Win64; x64
|
||||
- Macintosh; Intel Mac OS X 10_15_7
|
||||
- X11; Linux x86_64
|
||||
- X11; CrOS x86_64 14541.0.0
|
||||
- Fuchsia
|
||||
|
||||
# Mobile & Tablet: (except iOS/Android WebView)
|
||||
---
|
||||
Format:
|
||||
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<majorVersion>.0.0.0 <deviceCompat> Safari/537.36
|
||||
|
||||
Possible <deviceCompat> values:
|
||||
- "Mobile"
|
||||
- "" (empty string for Tablets & Desktop)
|
||||
*/
|
||||
|
||||
const isFrozenUA = ua => /^Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36$/.test(ua);
|
||||
|
||||
const unfreezeUA = async (ua, ch) => {
|
||||
const env = typeof navigator == 'undefined' ? 'node' : 'browser';
|
||||
if (env == 'node') {
|
||||
if (!ua['user-agent']) {
|
||||
throw new Error('User-Agent header not found');
|
||||
}
|
||||
ch = UACHParser(ua);
|
||||
ua = ua['user-agent'];
|
||||
} else {
|
||||
ua = ua || navigator.userAgent;
|
||||
ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
|
||||
}
|
||||
if (isFrozenUA(ua) && ch) {
|
||||
switch (ch.platform) {
|
||||
case 'Windows':
|
||||
let [major, minor] = ch.platformVersion
|
||||
.split('.')
|
||||
.map(num => parseInt(num, 10));
|
||||
major = (major < 1) ? '6' : (major >= 13) ? '11' : '10';
|
||||
ua = ua .replace(/(?<OS>Windows NT) 10\.0/, `$<OS> ${major}.${minor}`)
|
||||
.replace(/; (?<ARCH>Win64; x64)/,
|
||||
(ch.architecture == 'arm') ?
|
||||
'; ARM' :
|
||||
(ch.wow64) ?
|
||||
'; WOW64' :
|
||||
(ch.architecture == 'x86' && ch.bitness != '64') ?
|
||||
'' : '; $<ARCH>');
|
||||
break;
|
||||
case 'Android':
|
||||
ua = ua.replace(/(?<OS>Android) 10; K/, `$<OS> ${ch.platformVersion}; ${ch.model}`);
|
||||
break;
|
||||
case 'Linux':
|
||||
case 'Chrome OS':
|
||||
ua = ua.replace(/(?<ARCH>x86_64)/,
|
||||
(ch.architecture == 'arm') ?
|
||||
((ch.bitness == '64') ? 'arm64' : 'arm') :
|
||||
(ch.architecture == 'x86' && ch.bitness != '64') ?
|
||||
'x86' : '$<ARCH>');
|
||||
break;
|
||||
case 'macOS':
|
||||
ua = ua.replace(/(?<OS>Mac OS X) 10_15_7/, `$<OS> ${ch.platformVersion.replace(/\./, '_')}`);
|
||||
break;
|
||||
}
|
||||
if (ch.fullVersionList) {
|
||||
ua = ua.replace(/Chrome\/\d+\.0\.0\.0 /,
|
||||
ch.fullVersionList
|
||||
.filter(browser => !/not.a.brand/i.test(browser.brand))
|
||||
.map(browser => `${browser.brand.replace(/^google /i,'')}/${browser.version} `)
|
||||
.join(''));
|
||||
}
|
||||
}
|
||||
return ua;
|
||||
};
|
||||
|
||||
export {
|
||||
isFrozenUA,
|
||||
unfreezeUA
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
const { isFrozenUA, unfreezeUA, UACHParser } = require('@ua-parser-js/helpers');
|
||||
const { isFrozenUA, unfreezeUA } = require('@ua-parser-js/user-agent-helpers');
|
||||
const { UACHParser } = require('@ua-parser-js/client-hints-helpers');
|
||||
const assert = require('assert');
|
||||
|
||||
describe('isFrozenUA()', () => {
|
||||
@ -54,7 +55,7 @@ describe('unfreezeUA()', () => {
|
||||
});
|
||||
|
||||
describe('UACHParser()', () => {
|
||||
it('parse client hints HTTP headers (sec-ch-ua) into a JavaScript object', () => {
|
||||
it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => {
|
||||
assert.deepEqual(UACHParser(headers), {
|
||||
"architecture": "arm",
|
||||
"bitness": "64",
|
||||
|
@ -1,47 +0,0 @@
|
||||
// @ts-check
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { unfreezeUA } from '@ua-parser-js/helpers';
|
||||
|
||||
test('test for unfreezeUA() method', async ({ page }) => {
|
||||
|
||||
|
||||
await page.addInitScript(() => {
|
||||
Object.defineProperty(navigator, 'userAgentData', {
|
||||
value: {
|
||||
brands: [],
|
||||
platform: 'Windows',
|
||||
mobile: false,
|
||||
getHighEntropyValues: () => {
|
||||
return Promise.resolve({
|
||||
architecture: 'x86',
|
||||
bitness: '64',
|
||||
fullVersionList: [
|
||||
{
|
||||
brand: 'New Browser',
|
||||
version: '110.1.2.3'
|
||||
},
|
||||
{
|
||||
brand: 'Chromium',
|
||||
version: '110.1.2.3'
|
||||
},
|
||||
{
|
||||
brand: 'Not(A:Brand',
|
||||
version: '110'
|
||||
}
|
||||
],
|
||||
platform: 'Windows',
|
||||
platformVersion: '0.3'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(navigator, 'userAgent', {
|
||||
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36'
|
||||
});
|
||||
});
|
||||
await page.goto('about:blank');
|
||||
await page.addScriptTag({ path: './src/helpers/ua-parser-helpers.js' });
|
||||
// @ts-ignore
|
||||
const ua = await page.evaluate(async () => await unfreezeUA());
|
||||
expect(ua).toBe('Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) New Browser/110.1.2.3 Chromium/110.1.2.3 Safari/537.36');
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user