mirror of
https://github.com/faisalman/ua-parser-js.git
synced 2025-09-27 07:58:45 +03:00
Remove sub-packages since they've been moved to their own dedicated repo
https://github.com/faisalman/ua-client-hints-js https://github.com/faisalman/gpu-detect-js https://github.com/faisalman/ua-is-frozen https://github.com/faisalman/re-parse-js
This commit is contained in:
parent
385e0aaee5
commit
5226361348
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"commonjs": true,
|
|
||||||
"es2021": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": "latest"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
}
|
|
||||||
}
|
|
@ -182,7 +182,6 @@
|
|||||||
"@babel/traverse": "7.15.4",
|
"@babel/traverse": "7.15.4",
|
||||||
"@jazzer.js/core": "^1.4.0",
|
"@jazzer.js/core": "^1.4.0",
|
||||||
"@playwright/test": "~1.32.2",
|
"@playwright/test": "~1.32.2",
|
||||||
"eslint": "^8.48.0",
|
|
||||||
"jshint": "~2.13.6",
|
"jshint": "~2.13.6",
|
||||||
"mocha": "~8.2.0",
|
"mocha": "~8.2.0",
|
||||||
"requirejs": "2.3.2",
|
"requirejs": "2.3.2",
|
||||||
@ -219,10 +218,5 @@
|
|||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/faisalman"
|
"url": "https://github.com/sponsors/faisalman"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"workspaces": [
|
|
||||||
"src/gpu-detect",
|
|
||||||
"src/ua-client-hints",
|
|
||||||
"src/user-agent-helpers"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -46,24 +46,6 @@ const modules = [
|
|||||||
dest : 'src/extensions/ua-parser-extensions.mjs',
|
dest : 'src/extensions/ua-parser-extensions.mjs',
|
||||||
title : 'ua-parser-js/extensions',
|
title : 'ua-parser-js/extensions',
|
||||||
replacements : []
|
replacements : []
|
||||||
},
|
|
||||||
{
|
|
||||||
src : 'src/gpu-detect/gpu-detect.js',
|
|
||||||
dest : 'src/gpu-detect/gpu-detect.mjs',
|
|
||||||
title : '@ua-parser-js/gpu-detect',
|
|
||||||
replacements : []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src : 'src/user-agent-helpers/user-agent-helpers.js',
|
|
||||||
dest : 'src/user-agent-helpers/user-agent-helpers.mjs',
|
|
||||||
title : '@ua-parser-js/user-agent-helpers',
|
|
||||||
replacements : []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src : 'src/ua-client-hints/ua-client-hints.js',
|
|
||||||
dest : 'src/ua-client-hints/ua-client-hints.mjs',
|
|
||||||
title : '@ua-parser-js/ua-client-hints',
|
|
||||||
replacements : []
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ echo '
|
|||||||
- lint js code
|
- lint js code
|
||||||
'
|
'
|
||||||
npm run test:jshint || exit 1
|
npm run test:jshint || exit 1
|
||||||
npm run test:eslint || exit 1
|
#npm run test:eslint || exit 1
|
||||||
|
|
||||||
echo '
|
echo '
|
||||||
- test using mocha
|
- test using mocha
|
||||||
|
3
src/gpu-detect/gpu-detect.d.ts
vendored
3
src/gpu-detect/gpu-detect.d.ts
vendored
@ -1,3 +0,0 @@
|
|||||||
export class GPUDetect {
|
|
||||||
static getGPU: { vendor: string, model: string }
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
//////////////////////////////////////////////
|
|
||||||
/* Extracts GPU information from user-agent
|
|
||||||
https://github.com/faisalman/ua-parser-js
|
|
||||||
Author: Faisal Salman <f@faisalman.com>
|
|
||||||
MIT License */
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
|
|
||||||
/*jshint esversion: 11 */
|
|
||||||
|
|
||||||
const rendererMap = [
|
|
||||||
[[
|
|
||||||
/(intel).*\b(hd\sgraphics\s\d{4}|iris(?:\spro)|gma\s\w+)/i, // Intel
|
|
||||||
/(nvidia)\s(geforce\s(?:gtx?\s)\d\w+|quadro)/i, // NVIDIA
|
|
||||||
/\b(sis)\s(\w+)/i // SiS
|
|
||||||
], ['vendor', 'model']],
|
|
||||||
|
|
||||||
[[
|
|
||||||
/\b(radeon[\shdr\d]+\w{4,5})/i // ATI/AMD
|
|
||||||
], ['model', ['vendor', 'AMD']]],
|
|
||||||
|
|
||||||
[[
|
|
||||||
/(adreno\s(?:\(tm\)\s)\w+)/i // Qualcomm
|
|
||||||
], [['model', /\(tm\)\s/i, ''], ['vendor', 'Qualcomm']]]
|
|
||||||
];
|
|
||||||
|
|
||||||
const vendorMap = [
|
|
||||||
[[
|
|
||||||
/\b(amd|apple|arm|ati|img|intel|nvidia|qualcomm|samsung|sis)\b/i
|
|
||||||
], ['vendor']]
|
|
||||||
];
|
|
||||||
|
|
||||||
class RegexMap {
|
|
||||||
|
|
||||||
static parse(str, mapper) {
|
|
||||||
let res = {};
|
|
||||||
if (typeof str === 'string') {
|
|
||||||
for (const [regs, props] of mapper) {
|
|
||||||
if (!Array.isArray(regs)) {
|
|
||||||
throw new Error('RegexMap: Expect Array of RegExp');
|
|
||||||
}
|
|
||||||
if (!Array.isArray(props)) {
|
|
||||||
throw new Error('RegexMap: Expect Array for Properties Mapping');
|
|
||||||
}
|
|
||||||
for (const reg of regs) {
|
|
||||||
if (!reg instanceof RegExp) {
|
|
||||||
throw new Error('RegexMap: Expect RegExp Instance');
|
|
||||||
}
|
|
||||||
const matches = reg.exec(str);
|
|
||||||
if (matches) {
|
|
||||||
props.forEach((prop, idx) => {
|
|
||||||
const val = matches[idx+1];
|
|
||||||
if (Array.isArray(prop)) {
|
|
||||||
const key = prop[0];
|
|
||||||
if (typeof key !== 'string') {
|
|
||||||
throw new Error('RegexMap: Expect String Input');
|
|
||||||
}
|
|
||||||
if (prop.length == 2) {
|
|
||||||
if (typeof prop[1] === 'string') {
|
|
||||||
res[key] = prop[1];
|
|
||||||
} else if (typeof prop[1] === 'function') {
|
|
||||||
res[key] = prop[1].call(res, val);
|
|
||||||
}
|
|
||||||
} else if (prop.length == 3) {
|
|
||||||
if (prop[1] instanceof RegExp) {
|
|
||||||
res[key] = val.replace(prop[1], prop[2]);
|
|
||||||
} else if (typeof prop[1] === 'function') {
|
|
||||||
res[key] = prop[1].call(res, val, prop[2]);
|
|
||||||
}
|
|
||||||
} else if (prop.length == 4) {
|
|
||||||
res[key] = prop[3].call(res, val.replace(prop[1], prop[2]));
|
|
||||||
} else {
|
|
||||||
res[key] = val;
|
|
||||||
}
|
|
||||||
} else if (typeof prop === 'string') {
|
|
||||||
res[prop] = val;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (res) return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class GPUDetect {
|
|
||||||
|
|
||||||
static getGPU (strRenderer, strVendor) {
|
|
||||||
|
|
||||||
let gpuInfo = { vendor : undefined, model : undefined };
|
|
||||||
|
|
||||||
if (typeof strRenderer !== 'string') {
|
|
||||||
if (globalThis.document) {
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
const gl = canvas.getContext('webgl2') ||
|
|
||||||
canvas.getContext('webgl') ||
|
|
||||||
canvas.getContext('experimental-webgl');
|
|
||||||
if (gl) {
|
|
||||||
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
|
|
||||||
strVendor = gl.getParameter(debugInfo?.UNMASKED_VENDOR_WEBGL);
|
|
||||||
strRenderer = gl.getParameter(debugInfo?.UNMASKED_RENDERER_WEBGL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (strRenderer || strVendor) {
|
|
||||||
({ vendor : gpuInfo.vendor, model : gpuInfo.model } = RegexMap.parse(strRenderer, rendererMap));
|
|
||||||
gpuInfo.vendor = gpuInfo.vendor ?? RegexMap.parse(strVendor, rendererMap)?.vendor ?? RegexMap.parse(strVendor, vendorMap)?.vendor;
|
|
||||||
}
|
|
||||||
return gpuInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
GPUDetect
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "User-Agent GPU Info",
|
|
||||||
"name": "@ua-parser-js/gpu-detect",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"author": "Faisal Salman <f@faisalman.com>",
|
|
||||||
"description": "Extracts GPU information from user-agent",
|
|
||||||
"main": "gpu-detect.js",
|
|
||||||
"module": "gpu-detect.mjs",
|
|
||||||
"scripts": {
|
|
||||||
"test": "mocha ../../test/mocha-test-helpers"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/faisalman/ua-parser-js.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"ua-parser-js",
|
|
||||||
"gpu-detection"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/faisalman/ua-parser-js/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/faisalman/ua-parser-js#readme"
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
# @ua-parser-js/gpu-detect
|
|
||||||
|
|
||||||
This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that extracts GPU information from user-agent.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm i @ua-parser-js/gpu-detect
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Example
|
|
||||||
|
|
||||||
// in browser environment
|
|
||||||
const { vendor, model } = GPUDetect.getGPU();
|
|
||||||
|
|
||||||
// in non-browser environment
|
|
||||||
const { vendor, model } = GPUDetect.getGPU("AMD Radeon");
|
|
||||||
```
|
|
@ -1,4 +0,0 @@
|
|||||||
const { GPUDetect } = require('../gpu-detect.js');
|
|
||||||
|
|
||||||
console.log(GPUDetect.getGPU('AMD Radeon R9 M295X OpenGL Engine'));
|
|
||||||
console.log(GPUDetect.getGPU('','ATI Technologies Inc.'));
|
|
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "User-Agent Client Hints",
|
|
||||||
"name": "@ua-parser-js/ua-client-hints",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"author": "Faisal Salman <f@faisalman.com>",
|
|
||||||
"description": "A collection of utility methods for working with user-agent client hints",
|
|
||||||
"main": "ua-client-hints.js",
|
|
||||||
"module": "ua-client-hints.mjs",
|
|
||||||
"scripts": {
|
|
||||||
"test": "mocha ./test/*"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/faisalman/ua-parser-js.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"ua-parser-js",
|
|
||||||
"browser-detection",
|
|
||||||
"device-detection",
|
|
||||||
"os-detection",
|
|
||||||
"user-agent",
|
|
||||||
"client-hints"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/faisalman/ua-parser-js/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/faisalman/ua-parser-js#readme"
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
# @ua-parser-js/ua-client-hints
|
|
||||||
|
|
||||||
This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent client hints.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm i @ua-parser-js/ua-client-hints
|
|
||||||
```
|
|
||||||
|
|
||||||
### * `getUAData([props:array]):object`
|
|
||||||
|
|
||||||
Get user-agent client hints values of current instance in form of JS object representation
|
|
||||||
|
|
||||||
### * `setUAData([uaData:object]):UAClientHints`
|
|
||||||
|
|
||||||
Set values of user-agent client hints for the current instance either from navigator.userAgentData or from HTTP headers (Sec-CH-UA-*)
|
|
||||||
|
|
||||||
### * `getSerializedUAData([props:array]):object`
|
|
||||||
|
|
||||||
Get user-agent client hints values of current instance in form of HTTP headers string representation (Sec-CH-UA-*)
|
|
||||||
|
|
||||||
## Code Example
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { UAClientHints } from '@ua-parser-js/ua-client-hints';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Suppose we're in a server having this client hints data:
|
|
||||||
|
|
||||||
const httpHeaders = {
|
|
||||||
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
|
|
||||||
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
|
|
||||||
'sec-ch-ua-arch' : 'arm',
|
|
||||||
'sec-ch-ua-bitness' : '64',
|
|
||||||
'sec-ch-ua-mobile' : '?1',
|
|
||||||
'sec-ch-ua-model' : 'Pixel 99',
|
|
||||||
'sec-ch-ua-platform' : 'Linux',
|
|
||||||
'sec-ch-ua-platform-version' : '13'
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
const uaCH = new UAClientHints();
|
|
||||||
uaCH.setUAData(httpHeaders);
|
|
||||||
const uaCHData1 = uaCH.getUAData();
|
|
||||||
const uaCHData2 = uaCH.getUAData(['architecture', 'bitness']);
|
|
||||||
|
|
||||||
console.log(uaCHData1);
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"architecture": "arm",
|
|
||||||
"bitness": "64",
|
|
||||||
"brands": [
|
|
||||||
{
|
|
||||||
"brand": "Chromium",
|
|
||||||
"version": "93"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"brand": "Google Chrome",
|
|
||||||
"version": "93"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"brand": " Not;A Brand",
|
|
||||||
"version": "99"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fullVersionList": [
|
|
||||||
{
|
|
||||||
"brand": "Chromium",
|
|
||||||
"version": "93.0.1.2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"brand": "Google Chrome",
|
|
||||||
"version": "93.0.1.2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"brand": " Not;A Brand",
|
|
||||||
"version": "99.0.1.2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mobile": true,
|
|
||||||
"model": "Pixel 99",
|
|
||||||
"platform": "Linux",
|
|
||||||
"platformVersion": "13",
|
|
||||||
"wow64": null,
|
|
||||||
"formFactor": null
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
console.log(uaCHData2);
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"architecture": "arm",
|
|
||||||
"bitness": "64"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
uaCH.setUAData({
|
|
||||||
"wow64" : true,
|
|
||||||
"formFactor" : "Automotive"
|
|
||||||
});
|
|
||||||
|
|
||||||
const headersData1 = uaCH.getSerializedUAData();
|
|
||||||
const headersData2 = uaCH.getSerializedUAData(['brand', 'mobile', 'model']);
|
|
||||||
|
|
||||||
console.log(headersData1);
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
|
|
||||||
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
|
|
||||||
'sec-ch-ua-arch' : 'arm',
|
|
||||||
'sec-ch-ua-bitness' : '64',
|
|
||||||
'sec-ch-ua-mobile' : '?1',
|
|
||||||
'sec-ch-ua-model' : 'Pixel 99',
|
|
||||||
'sec-ch-ua-platform' : 'Linux',
|
|
||||||
'sec-ch-ua-platform-version' : '13',
|
|
||||||
'sec-ch-ua-wow64' : '?1',
|
|
||||||
'sec-ch-ua-form-factor' : 'Automotive'
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
console.log(headersData2);
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
|
|
||||||
'sec-ch-ua-mobile' : '?1',
|
|
||||||
'sec-ch-ua-model' : 'Pixel 99'
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
```
|
|
@ -1,64 +0,0 @@
|
|||||||
const { UAClientHints } = require('../ua-client-hints');
|
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
describe('Parse CH Headers', () => {
|
|
||||||
it('parse client hints HTTP headers (sec-ch-ua) into a client hints-like JavaScript object', () => {
|
|
||||||
|
|
||||||
const req = {
|
|
||||||
headers : {
|
|
||||||
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
|
|
||||||
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
|
|
||||||
'sec-ch-ua-arch' : '"arm"',
|
|
||||||
'sec-ch-ua-bitness' : '"64"',
|
|
||||||
'sec-ch-ua-mobile' : '?1',
|
|
||||||
'sec-ch-ua-model' : '"Pixel 99"',
|
|
||||||
'sec-ch-ua-platform' : '"Linux"',
|
|
||||||
'sec-ch-ua-platform-version' : '"13"',
|
|
||||||
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
|
|
||||||
}};
|
|
||||||
|
|
||||||
assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(['architecture', 'bitness']), {
|
|
||||||
"architecture": "arm",
|
|
||||||
"bitness": "64"
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(new UAClientHints().setUAData(req.headers).getUAData(), {
|
|
||||||
"architecture": "arm",
|
|
||||||
"bitness": "64",
|
|
||||||
"brands": [
|
|
||||||
{
|
|
||||||
"brand": "Chromium",
|
|
||||||
"version": "93"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"brand": "Google Chrome",
|
|
||||||
"version": "93"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"brand": "Not;A Brand",
|
|
||||||
"version": "99"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fullVersionList": [
|
|
||||||
{
|
|
||||||
"brand": "Chromium",
|
|
||||||
"version": "93.0.1.2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"brand": "Google Chrome",
|
|
||||||
"version": "93.0.1.2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"brand": "Not;A Brand",
|
|
||||||
"version": "99.0.1.2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"formFactor": null,
|
|
||||||
"mobile": true,
|
|
||||||
"model": "Pixel 99",
|
|
||||||
"platform": "Linux",
|
|
||||||
"platformVersion": "13",
|
|
||||||
"wow64": null
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
39
src/ua-client-hints/ua-client-hints.d.ts
vendored
39
src/ua-client-hints/ua-client-hints.d.ts
vendored
@ -1,39 +0,0 @@
|
|||||||
export type UABrowser = {
|
|
||||||
brand: string | null,
|
|
||||||
version: string | null
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UADataType = boolean | string | Array<UABrowser> | null;
|
|
||||||
export type UADataField =
|
|
||||||
'brands' |
|
|
||||||
'mobile' |
|
|
||||||
'platform' |
|
|
||||||
'architecture' |
|
|
||||||
'bitness' |
|
|
||||||
'formFactor' |
|
|
||||||
'fullVersionList' |
|
|
||||||
'model' |
|
|
||||||
'platformVersion' |
|
|
||||||
'wow64';
|
|
||||||
|
|
||||||
export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list';
|
|
||||||
export type HeaderField =
|
|
||||||
'sec-ch-ua-arch' |
|
|
||||||
'sec-ch-ua-bitness' |
|
|
||||||
'sec-ch-ua' |
|
|
||||||
'sec-ch-ua-form-factor' |
|
|
||||||
'sec-ch-ua-full-version-list' |
|
|
||||||
'sec-ch-ua-mobile' |
|
|
||||||
'sec-ch-ua-model' |
|
|
||||||
'sec-ch-ua-platform' |
|
|
||||||
'sec-ch-ua-platform-version' |
|
|
||||||
'sec-ch-ua-wow64';
|
|
||||||
|
|
||||||
export class UAClientHints {
|
|
||||||
#ch: Map<UADataField, UADataType>;
|
|
||||||
#parseHeader(str: string, type: HeaderType): UADataType;
|
|
||||||
#serialize(data: UADataType, type: HeaderType): string;
|
|
||||||
getSerializedUAData(): Record<HeaderField, string>;
|
|
||||||
getUAData(props?: Array<UADataField>): Record<UADataField, UADataType>;
|
|
||||||
setUAData(uaData: Record<UADataField, UADataType> | Record<HeaderField, string>): UAClientHints;
|
|
||||||
};
|
|
@ -1,130 +0,0 @@
|
|||||||
//////////////////////////////////////////////
|
|
||||||
/* A collection of utility methods for
|
|
||||||
working with user-agent client hints
|
|
||||||
https://github.com/faisalman/ua-parser-js
|
|
||||||
Author: Faisal Salman <f@faisalman.com>
|
|
||||||
MIT License */
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
|
|
||||||
/*jshint esversion: 11 */
|
|
||||||
|
|
||||||
const fieldType = Object.freeze({
|
|
||||||
Boolean : 'sf-boolean',
|
|
||||||
List : 'sf-list',
|
|
||||||
String : 'sf-string'
|
|
||||||
});
|
|
||||||
|
|
||||||
const uaCHMap = Object.freeze({
|
|
||||||
architecture : {
|
|
||||||
field : 'Sec-CH-UA-Arch',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
bitness : {
|
|
||||||
field : 'Sec-CH-UA-Bitness',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
brands : {
|
|
||||||
field : 'Sec-CH-UA',
|
|
||||||
type : fieldType.List
|
|
||||||
},
|
|
||||||
formFactor : {
|
|
||||||
field : 'Sec-CH-UA-Form-Factor',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
fullVersionList : {
|
|
||||||
field : 'Sec-CH-UA-Full-Version-List',
|
|
||||||
type : fieldType.List
|
|
||||||
},
|
|
||||||
mobile : {
|
|
||||||
field : 'Sec-CH-UA-Mobile',
|
|
||||||
type : fieldType.Boolean
|
|
||||||
},
|
|
||||||
model : {
|
|
||||||
field : 'Sec-CH-UA-Model',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
platform : {
|
|
||||||
field : 'Sec-CH-UA-Platform',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
platformVersion : {
|
|
||||||
field : 'Sec-CH-UA-Platform-Version',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
wow64 : {
|
|
||||||
field : 'Sec-CH-UA-WOW64',
|
|
||||||
type : fieldType.Boolean
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
class UAClientHints {
|
|
||||||
|
|
||||||
#uach = new Map();
|
|
||||||
|
|
||||||
constructor () {
|
|
||||||
for (const key in uaCHMap) {
|
|
||||||
this.#uach.set(key, null);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
#parseHeader (str, type) {
|
|
||||||
if (!str) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case fieldType.Boolean:
|
|
||||||
return /\?1/.test(str);
|
|
||||||
case fieldType.List:
|
|
||||||
return str.replace(/\\?\"/g, '')
|
|
||||||
.split(',')
|
|
||||||
.map(brands => {
|
|
||||||
const [brand, version] = brands.trim().split(';v=');
|
|
||||||
return {
|
|
||||||
brand : brand,
|
|
||||||
version : version
|
|
||||||
};
|
|
||||||
});
|
|
||||||
case fieldType.String:
|
|
||||||
return str.replace(/\s*\\?\"\s*/g, '');
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#serialize(data, type) {
|
|
||||||
throw new Error('Not implemented yet');
|
|
||||||
//return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
getSerializedUAData() {
|
|
||||||
throw new Error('Not implemented yet');
|
|
||||||
//let http = {};
|
|
||||||
//return http;
|
|
||||||
}
|
|
||||||
|
|
||||||
getUAData(props) {
|
|
||||||
if (props) {
|
|
||||||
return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)]));
|
|
||||||
}
|
|
||||||
return Object.fromEntries(this.#uach);
|
|
||||||
}
|
|
||||||
|
|
||||||
setUAData(uaDataValues) {
|
|
||||||
if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) {
|
|
||||||
for (const val in uaCHMap) {
|
|
||||||
const { field, type } = uaCHMap[val];
|
|
||||||
this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const value in uaDataValues) {
|
|
||||||
if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
UAClientHints
|
|
||||||
};
|
|
@ -1,134 +0,0 @@
|
|||||||
// Generated ESM version of @ua-parser-js/ua-client-hints
|
|
||||||
// DO NOT EDIT THIS FILE!
|
|
||||||
// Source: /src/ua-client-hints/ua-client-hints.js
|
|
||||||
|
|
||||||
//////////////////////////////////////////////
|
|
||||||
/* A collection of utility methods for
|
|
||||||
working with user-agent client hints
|
|
||||||
https://github.com/faisalman/ua-parser-js
|
|
||||||
Author: Faisal Salman <f@faisalman.com>
|
|
||||||
MIT License */
|
|
||||||
/////////////////////////////////////////////
|
|
||||||
|
|
||||||
/*jshint esversion: 11 */
|
|
||||||
|
|
||||||
const fieldType = Object.freeze({
|
|
||||||
Boolean : 'sf-boolean',
|
|
||||||
List : 'sf-list',
|
|
||||||
String : 'sf-string'
|
|
||||||
});
|
|
||||||
|
|
||||||
const uaCHMap = Object.freeze({
|
|
||||||
architecture : {
|
|
||||||
field : 'Sec-CH-UA-Arch',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
bitness : {
|
|
||||||
field : 'Sec-CH-UA-Bitness',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
brands : {
|
|
||||||
field : 'Sec-CH-UA',
|
|
||||||
type : fieldType.List
|
|
||||||
},
|
|
||||||
formFactor : {
|
|
||||||
field : 'Sec-CH-UA-Form-Factor',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
fullVersionList : {
|
|
||||||
field : 'Sec-CH-UA-Full-Version-List',
|
|
||||||
type : fieldType.List
|
|
||||||
},
|
|
||||||
mobile : {
|
|
||||||
field : 'Sec-CH-UA-Mobile',
|
|
||||||
type : fieldType.Boolean
|
|
||||||
},
|
|
||||||
model : {
|
|
||||||
field : 'Sec-CH-UA-Model',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
platform : {
|
|
||||||
field : 'Sec-CH-UA-Platform',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
platformVersion : {
|
|
||||||
field : 'Sec-CH-UA-Platform-Version',
|
|
||||||
type : fieldType.String
|
|
||||||
},
|
|
||||||
wow64 : {
|
|
||||||
field : 'Sec-CH-UA-WOW64',
|
|
||||||
type : fieldType.Boolean
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
class UAClientHints {
|
|
||||||
|
|
||||||
#uach = new Map();
|
|
||||||
|
|
||||||
constructor () {
|
|
||||||
for (const key in uaCHMap) {
|
|
||||||
this.#uach.set(key, null);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
#parseHeader (str, type) {
|
|
||||||
if (!str) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case fieldType.Boolean:
|
|
||||||
return /\?1/.test(str);
|
|
||||||
case fieldType.List:
|
|
||||||
return str.replace(/\\?\"/g, '')
|
|
||||||
.split(',')
|
|
||||||
.map(brands => {
|
|
||||||
const [brand, version] = brands.trim().split(';v=');
|
|
||||||
return {
|
|
||||||
brand : brand,
|
|
||||||
version : version
|
|
||||||
};
|
|
||||||
});
|
|
||||||
case fieldType.String:
|
|
||||||
return str.replace(/\s*\\?\"\s*/g, '');
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#serialize(data, type) {
|
|
||||||
throw new Error('Not implemented yet');
|
|
||||||
//return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
getSerializedUAData() {
|
|
||||||
throw new Error('Not implemented yet');
|
|
||||||
//let http = {};
|
|
||||||
//return http;
|
|
||||||
}
|
|
||||||
|
|
||||||
getUAData(props) {
|
|
||||||
if (props) {
|
|
||||||
return Object.fromEntries(props.filter(val => this.#uach.get(val)).map(val => [val, this.#uach.get(val)]));
|
|
||||||
}
|
|
||||||
return Object.fromEntries(this.#uach);
|
|
||||||
}
|
|
||||||
|
|
||||||
setUAData(uaDataValues) {
|
|
||||||
if(Object.keys(uaDataValues).some(key => key.startsWith('sec-ch-ua'))) {
|
|
||||||
for (const val in uaCHMap) {
|
|
||||||
const { field, type } = uaCHMap[val];
|
|
||||||
this.#uach.set(val, this.#parseHeader(uaDataValues[field.toLowerCase()], type));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const value in uaDataValues) {
|
|
||||||
if (this.#uach.has(value)) this.#uach.set(value, uaDataValues[value]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
UAClientHints
|
|
||||||
};
|
|
@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "User-Agent Helpers",
|
|
||||||
"name": "@ua-parser-js/user-agent-helpers",
|
|
||||||
"version": "0.0.2",
|
|
||||||
"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/*"
|
|
||||||
},
|
|
||||||
"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": "*"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
# @ua-parser-js/user-agent-helpers
|
|
||||||
|
|
||||||
This is a [UAParser.js](https://github.com/faisalman/ua-parser-js) module that contains a collection of utility methods for working with user-agent.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm i @ua-parser-js/user-agent-helpers
|
|
||||||
```
|
|
||||||
|
|
||||||
### * `isFrozenUA(ua:string):boolean`
|
|
||||||
|
|
||||||
Check whether a user-agent string match with [frozen user-agent pattern](https://www.chromium.org/updates/ua-reduction/)
|
|
||||||
|
|
||||||
### * `unfreezeUA([ua:string,ch:object]|[headers:object]):Promise<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'
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Alternatively:
|
|
||||||
const ua = navigator.userAgent;
|
|
||||||
const ch = await navigator.userAgentData.getHighEntropyValues();
|
|
||||||
const newUA = await unfreezeUA(ua, ch);
|
|
||||||
|
|
||||||
// Server environment:
|
|
||||||
const newUA = await unfreezeUA(req.headers);
|
|
||||||
*/
|
|
||||||
```
|
|
@ -1,56 +0,0 @@
|
|||||||
const { isFrozenUA, unfreezeUA } = require('../user-agent-helpers');
|
|
||||||
const { UAClientHints } = require('@ua-parser-js/ua-client-hints');
|
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
describe('isFrozenUA()', () => {
|
|
||||||
it('Returns whether a user agent is frozen', () => {
|
|
||||||
|
|
||||||
const regularWindowsUA = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
|
|
||||||
const freezedWindowsUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36";
|
|
||||||
|
|
||||||
const regularMacUA = "";
|
|
||||||
const freezedMacUA = "";
|
|
||||||
|
|
||||||
const regularLinuxUA = "";
|
|
||||||
const freezedLinuxUA = "";
|
|
||||||
|
|
||||||
const regularCrOSUA = "";
|
|
||||||
const freezedCrOSUA = "";
|
|
||||||
|
|
||||||
const regularFuchsiaUA = "";
|
|
||||||
const freezedFuchsiaUA = "";
|
|
||||||
|
|
||||||
const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
|
|
||||||
const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
|
|
||||||
|
|
||||||
const regularTabletUA = "Mozilla/5.0 (Linux; Android 9; SM-T810) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
|
|
||||||
const freezedTabletUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36";
|
|
||||||
|
|
||||||
assert.strictEqual(isFrozenUA(regularWindowsUA), false);
|
|
||||||
assert.strictEqual(isFrozenUA(freezedWindowsUA), true);
|
|
||||||
assert.strictEqual(isFrozenUA(regularMobileUA), false);
|
|
||||||
assert.strictEqual(isFrozenUA(freezedMobileUA), true);
|
|
||||||
assert.strictEqual(isFrozenUA(regularTabletUA), false);
|
|
||||||
assert.strictEqual(isFrozenUA(freezedTabletUA), true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const req = {
|
|
||||||
headers : {
|
|
||||||
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
|
|
||||||
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
|
|
||||||
'sec-ch-ua-arch' : '"arm"',
|
|
||||||
'sec-ch-ua-bitness' : '"64"',
|
|
||||||
'sec-ch-ua-mobile' : '?1',
|
|
||||||
'sec-ch-ua-model' : '"Pixel 99"',
|
|
||||||
'sec-ch-ua-platform' : '"Linux"',
|
|
||||||
'sec-ch-ua-platform-version' : '"13"',
|
|
||||||
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
|
|
||||||
}};
|
|
||||||
|
|
||||||
describe('unfreezeUA()', () => {
|
|
||||||
it('returns an unfreezed user-agent using real data from client hints HTTP headers (sec-ch-ua)', async () => {
|
|
||||||
const unfreezed = await unfreezeUA(req.headers);
|
|
||||||
assert.strictEqual(unfreezed, 'Mozilla/5.0 (X11; Linux arm64) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/93.0.1.2 Chrome/93.0.1.2 Safari/537.36');
|
|
||||||
});
|
|
||||||
});
|
|
35
src/user-agent-helpers/user-agent-helpers.d.ts
vendored
35
src/user-agent-helpers/user-agent-helpers.d.ts
vendored
@ -1,35 +0,0 @@
|
|||||||
export type UABrowser = {
|
|
||||||
brand: string | null,
|
|
||||||
version: string | null
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UADataType = boolean | string | Array<UABrowser> | null;
|
|
||||||
export type UADataField =
|
|
||||||
'brands' |
|
|
||||||
'mobile' |
|
|
||||||
'platform' |
|
|
||||||
'architecture' |
|
|
||||||
'bitness' |
|
|
||||||
'formFactor' |
|
|
||||||
'fullVersionList' |
|
|
||||||
'model' |
|
|
||||||
'platformVersion' |
|
|
||||||
'wow64';
|
|
||||||
|
|
||||||
export type HeaderType = 'sf-boolean' | 'sf-string' | 'sf-list';
|
|
||||||
export type HeaderField =
|
|
||||||
'sec-ch-ua-arch' |
|
|
||||||
'sec-ch-ua-bitness' |
|
|
||||||
'sec-ch-ua' |
|
|
||||||
'sec-ch-ua-form-factor' |
|
|
||||||
'sec-ch-ua-full-version-list' |
|
|
||||||
'sec-ch-ua-mobile' |
|
|
||||||
'sec-ch-ua-model' |
|
|
||||||
'sec-ch-ua-platform' |
|
|
||||||
'sec-ch-ua-platform-version' |
|
|
||||||
'sec-ch-ua-wow64';
|
|
||||||
|
|
||||||
export function isFrozenUA(ua: string): boolean;
|
|
||||||
export function unfreezeUA(): Promise<string>;
|
|
||||||
export function unfreezeUA(ua: string, ch: Record<UADataField, UADataType>): Promise<string>;
|
|
||||||
export function unfreezeUA(headers: Record<HeaderField, HeaderType>): Promise<string>;
|
|
@ -1,97 +0,0 @@
|
|||||||
////////////////////////////////////////////////////
|
|
||||||
/* 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 { UAClientHints } = require('@ua-parser-js/ua-client-hints');
|
|
||||||
|
|
||||||
/*
|
|
||||||
# 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 = new UAClientHints().setUAData(ua).getUAData();
|
|
||||||
ua = ua['user-agent'];
|
|
||||||
} else {
|
|
||||||
ua = ua || navigator.userAgent;
|
|
||||||
ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
|
|
||||||
}
|
|
||||||
if (isFrozenUA(ua) && ch) {
|
|
||||||
switch (ch.platform) {
|
|
||||||
case 'Windows':
|
|
||||||
let [major, minor] = ch.platformVersion
|
|
||||||
.split('.')
|
|
||||||
.map(num => parseInt(num, 10));
|
|
||||||
major = (major < 1) ? '6' : (major >= 13) ? '11' : '10';
|
|
||||||
ua = ua .replace(/(?<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;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
isFrozenUA,
|
|
||||||
unfreezeUA
|
|
||||||
};
|
|
@ -1,101 +0,0 @@
|
|||||||
// Generated ESM version of @ua-parser-js/user-agent-helpers
|
|
||||||
// DO NOT EDIT THIS FILE!
|
|
||||||
// Source: /src/user-agent-helpers/user-agent-helpers.js
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
/* A collection of utility methods for user-agent
|
|
||||||
https://github.com/faisalman/ua-parser-js
|
|
||||||
Author: Faisal Salman <f@faisalman.com>
|
|
||||||
MIT License */
|
|
||||||
///////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/*jshint esversion: 11 */
|
|
||||||
|
|
||||||
import { UAClientHints } from '@ua-parser-js/ua-client-hints';
|
|
||||||
|
|
||||||
/*
|
|
||||||
# Reference:
|
|
||||||
https://www.chromium.org/updates/ua-reduction/
|
|
||||||
|
|
||||||
# Desktop
|
|
||||||
---
|
|
||||||
Format:
|
|
||||||
Mozilla/5.0 (<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 = new UAClientHints().setUAData(ua).getUAData();
|
|
||||||
ua = ua['user-agent'];
|
|
||||||
} else {
|
|
||||||
ua = ua || navigator.userAgent;
|
|
||||||
ch = ch || await navigator.userAgentData?.getHighEntropyValues(['arch', 'bitness', 'fullVersionList', 'model', 'platform', 'platformVersion', 'wow64']);
|
|
||||||
}
|
|
||||||
if (isFrozenUA(ua) && ch) {
|
|
||||||
switch (ch.platform) {
|
|
||||||
case 'Windows':
|
|
||||||
let [major, minor] = ch.platformVersion
|
|
||||||
.split('.')
|
|
||||||
.map(num => parseInt(num, 10));
|
|
||||||
major = (major < 1) ? '6' : (major >= 13) ? '11' : '10';
|
|
||||||
ua = ua .replace(/(?<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
|
|
||||||
};
|
|
Loading…
x
Reference in New Issue
Block a user