express-useragent
Version:
JS Library & ExpressJS user-agent middleware exposing
1,269 lines (1,266 loc) • 41.9 kB
JavaScript
;
var expressUseragent = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/browser.ts
var browser_exports = {};
__export(browser_exports, {
UserAgent: () => UserAgent,
default: () => browser_default,
hydrate: () => hydrate,
parse: () => parse
});
// src/express-useragent.ts
var BOTS = [
"\\+https:\\/\\/developers.google.com\\/\\+\\/web\\/snippet\\/",
"ad\\smonitoring",
"adsbot",
"apex",
"applebot",
"archive.org_bot",
"baiduspider",
"bingbot",
"chromeheadless",
"cloudflare",
"cloudinary",
"crawler",
"curl",
"discordbot",
"duckduckbot",
"embedly",
"exabot",
"facebookexternalhit",
"facebot",
"flipboard",
"google",
"googlebot",
"gsa-crawler",
"gurujibot",
"guzzlehttp",
"heritrix",
"ia_archiver",
"insights",
"linkedinbot",
"ltx71",
"mediapartners",
"msnbot",
"odklbot",
"phantom\\.js",
"phantomjs",
"pingdom",
"pinterest",
"python",
"rtlnieuws",
"skypeuripreview",
"slackbot",
"slurp",
"spbot",
"telegrambot",
"test\\scertificate",
"testing",
"tiabot",
"tumblr ",
"twitterbot",
"vkshare",
"web\\sscraper",
"wget",
"yandexbot",
"whatsapp",
"orangebot",
"smtbot",
"qwantify",
"mj12bot",
"ahrefsbot",
"seznambot",
"panscient\\.com",
"duckduckgo-favicons-bot",
"uptimerobot",
"semrushbot",
"postman",
"dotbot",
"zoominfobot",
"ifttt",
"sogou",
"ru_bot",
"researchscan",
"nimbostratus-bot",
"slack-imgproxy",
"node-superagent",
"go-http-client",
"jersey",
"dataprovider.com",
"github-camo",
"dispatch",
"checkmarknetwork",
"screaming frog",
"whatweb",
"daum",
"netcraftsurveyagent",
"mojeekbot",
"surdotlybot",
"springbot"
];
var IS_BOT_REGEXP = new RegExp(`(${BOTS.join("|")})`, "i");
var SILK_REGEXP = /silk/i;
var SILK_ACCELERATED_REGEXP = /Silk-Accelerated=true/i;
var SMART_TV_REGEXP = /smart-tv|smarttv|googletv|appletv|hbbtv|pov_tv|netcast.tv/i;
var ANDROID_TABLET_REGEXP = /mobile/i;
var MOBILE_REGEXP = /mobile|^ios-/i;
var DALVIK_REGEXP = /dalvik/i;
var IOS_SCALE_REGEXP = /scale/i;
var OKHTTP_REGEXP = /okhttp/i;
var WEBKIT_REGEXP = /applewebkit/i;
var WECHAT_REGEXP = /micromessenger/i;
var DEFAULT_AGENT = {
isYaBrowser: false,
isAuthoritative: true,
isMobile: false,
isMobileNative: false,
isTablet: false,
isiPad: false,
isiPod: false,
isiPhone: false,
isiPhoneNative: false,
isAndroid: false,
isAndroidNative: false,
isBlackberry: false,
isOpera: false,
isIE: false,
isEdge: false,
isIECompatibilityMode: false,
isSafari: false,
isFirefox: false,
isWebkit: false,
isChrome: false,
isKonqueror: false,
isOmniWeb: false,
isSeaMonkey: false,
isFlock: false,
isAmaya: false,
isPhantomJS: false,
isEpiphany: false,
isDesktop: false,
isWindows: false,
isLinux: false,
isLinux64: false,
isMac: false,
isChromeOS: false,
isBada: false,
isSamsung: false,
isRaspberry: false,
isBot: false,
botName: "",
isCurl: false,
isAndroidTablet: false,
isWinJs: false,
isKindleFire: false,
isSilk: false,
isCaptive: false,
isSmartTV: false,
isUC: false,
isFacebook: false,
isAlamoFire: false,
isElectron: false,
silkAccelerated: false,
browser: "unknown",
version: "unknown",
os: "unknown",
platform: "unknown",
isWechat: false,
isWindowsPhone: false,
SilkAccelerated: false,
geoIp: {},
source: "",
electronVersion: "",
isDuckDuckGo: false,
clientHints: null
};
function createDefaultAgent() {
return {
...DEFAULT_AGENT,
geoIp: {},
source: "",
electronVersion: "",
botName: "",
clientHints: null
};
}
var isProductTokenChar = (charCode) => charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122 || charCode === 45 || charCode === 46 || charCode === 95;
var isDigit = (charCode) => charCode >= 48 && charCode <= 57;
var readProductToken = (source, startIndex = 0) => {
const slashIndex = source.indexOf("/", startIndex);
if (slashIndex <= startIndex) {
return null;
}
for (let index = startIndex; index < slashIndex; index += 1) {
if (!isProductTokenChar(source.charCodeAt(index))) {
return null;
}
}
let endIndex = slashIndex + 1;
while (endIndex < source.length && isProductTokenChar(source.charCodeAt(endIndex))) {
endIndex += 1;
}
if (endIndex === slashIndex + 1) {
return null;
}
return {
name: source.slice(startIndex, slashIndex),
version: source.slice(slashIndex + 1, endIndex)
};
};
var readVersionAfterProduct = (source, productName) => {
const token = readProductToken(source);
return (token == null ? void 0 : token.name.toLowerCase()) === productName.toLowerCase() ? token.version : null;
};
var readVersionAfterKnownPrefix = (source, prefixes) => {
const lowerSource = source.toLowerCase();
let bestStart = -1;
let bestPrefix = "";
for (const prefix of prefixes) {
const prefixStart = lowerSource.indexOf(prefix.toLowerCase());
if (prefixStart !== -1 && (bestStart === -1 || prefixStart < bestStart)) {
bestStart = prefixStart;
bestPrefix = prefix;
}
}
if (bestStart === -1) {
return null;
}
const versionStart = bestStart + bestPrefix.length;
let versionEnd = versionStart;
while (versionEnd < source.length && isProductTokenChar(source.charCodeAt(versionEnd))) {
versionEnd += 1;
}
return versionEnd > versionStart ? source.slice(versionStart, versionEnd) : null;
};
var readVersionAfterKnownProduct = (source, productNames) => readVersionAfterKnownPrefix(
source,
productNames.map((productName) => `${productName}/`)
);
var readInternetExplorerVersion = (source) => {
const lowerSource = source.toLowerCase();
const msieIndex = lowerSource.indexOf("msie");
if (msieIndex !== -1) {
let versionStart2 = msieIndex + 4;
while (source[versionStart2] === " ") {
versionStart2 += 1;
}
let versionEnd2 = versionStart2;
while (versionEnd2 < source.length) {
const charCode = source.charCodeAt(versionEnd2);
if (!isDigit(charCode) && charCode !== 46) {
break;
}
versionEnd2 += 1;
}
if (versionEnd2 > versionStart2) {
return source.slice(versionStart2, versionEnd2);
}
}
const tridentIndex = lowerSource.indexOf("trident/");
if (tridentIndex === -1) {
return null;
}
const rvIndex = lowerSource.indexOf("rv:", tridentIndex + 8);
if (rvIndex === -1) {
return null;
}
const versionStart = rvIndex + 3;
let versionEnd = versionStart;
while (versionEnd < source.length) {
const charCode = source.charCodeAt(versionEnd);
if (!isDigit(charCode) && charCode !== 46) {
break;
}
versionEnd += 1;
}
return versionEnd > versionStart ? source.slice(versionStart, versionEnd) : null;
};
var readTrailingProductToken = (source, requireClosingParen) => {
let endIndex = source.length;
while (endIndex > 0 && source.charCodeAt(endIndex - 1) === 32) {
endIndex -= 1;
}
if (source[endIndex - 1] === ")") {
endIndex -= 1;
} else if (requireClosingParen) {
return null;
}
let startIndex = endIndex;
while (startIndex > 0 && isProductTokenChar(source.charCodeAt(startIndex - 1))) {
startIndex -= 1;
}
return startIndex < endIndex ? source.slice(startIndex, endIndex) : null;
};
var readIOSDeviceOSMatch = (source, device) => {
const deviceLower = device.toLowerCase();
let searchFrom = 0;
while (searchFrom < source.length) {
const openIndex = source.indexOf("(", searchFrom);
if (openIndex === -1) {
return null;
}
const closeIndex = source.indexOf(")", openIndex + 1);
const segmentEnd = closeIndex === -1 ? source.length : closeIndex;
const segment = source.slice(openIndex + 1, segmentEnd);
const segmentLower = segment.toLowerCase();
if (segmentLower.startsWith(deviceLower)) {
let osSearchFrom = 0;
while (osSearchFrom < segmentLower.length) {
const osIndex = segmentLower.indexOf("os ", osSearchFrom);
if (osIndex === -1) {
break;
}
let versionStart = osIndex + 3;
while (segment[versionStart] === " ") {
versionStart += 1;
}
let majorEnd = versionStart;
while (majorEnd < segment.length && isDigit(segment.charCodeAt(majorEnd))) {
majorEnd += 1;
}
const separator = segment[majorEnd];
if (majorEnd > versionStart && (separator === "." || separator === "_")) {
let minorEnd = majorEnd + 1;
while (minorEnd < segment.length && isDigit(segment.charCodeAt(minorEnd))) {
minorEnd += 1;
}
if (minorEnd > majorEnd + 1) {
return `(${segment.slice(0, minorEnd)}`.replace("_", ".");
}
}
osSearchFrom = osIndex + 3;
}
}
searchFrom = segmentEnd + 1;
}
return null;
};
var DUCKDUCKGO_WEBKIT_REGEXP = /\sDdg\/\d+(?:\.\d+)*$/;
var _UserAgent = class _UserAgent {
constructor() {
this.versions = {
Edge: /(?:edge|edga|edgios|edg)\/([A-Za-z0-9_.-]+)/i,
Firefox: /(?:firefox|fxios)\/([A-Za-z0-9_.-]+)/i,
IE: /msie\s(\d+(?:\.\d+)?)|trident\/\d+(?:\.\d+)?;[^)]*\brv:(\d+(?:\.\d+)?)/i,
YaBrowser: /(?:yabrowser|yowser)\/([A-Za-z0-9_.-]+)/i,
Chrome: /(?:chrome|crios)\/([A-Za-z0-9_.-]+)/i,
Chromium: /chromium\/([A-Za-z0-9_.-]+)/i,
Safari: /(version|safari)\/([A-Za-z0-9_.-]+)/i,
Opera: /version\/([A-Za-z0-9_.-]+)|OPR\/([A-Za-z0-9_.-]+)/i,
Amaya: /amaya\/([A-Za-z0-9_.-]+)/i,
SeaMonkey: /seamonkey\/([A-Za-z0-9_.-]+)/i,
OmniWeb: /omniweb\/v([A-Za-z0-9_.-]+)/i,
Flock: /flock\/([A-Za-z0-9_.-]+)/i,
Epiphany: /epiphany\/([A-Za-z0-9_.-]+)/i,
WinJs: /msapphost\/([A-Za-z0-9_.-]+)/i,
PhantomJS: /phantomjs\/([A-Za-z0-9_.-]+)/i,
AlamoFire: /alamofire\/([A-Za-z0-9_.-]+)/i,
UC: /ucbrowser\/([A-Za-z0-9_.]+)/i,
Facebook: /FBAV\/([A-Za-z0-9_.]+)/i,
WebKit: /applewebkit\/([A-Za-z0-9_.]+)/i,
Wechat: /micromessenger\/([A-Za-z0-9_.]+)/i,
Electron: /Electron\/([A-Za-z0-9_.]+)/i,
DuckDuckGo: /\sDdg\/(\d+(?:\.\d+)*)$/i
};
this.browsers = {
YaBrowser: /yabrowser|yowser/i,
Edge: /edge|edga|edgios|edg/i,
Amaya: /amaya/i,
Konqueror: /konqueror/i,
Epiphany: /epiphany/i,
SeaMonkey: /seamonkey/i,
Flock: /flock/i,
OmniWeb: /omniweb/i,
Chromium: /chromium/i,
Chrome: /chrome|crios/i,
Safari: /safari/i,
IE: /msie|trident/i,
Opera: /opera|OPR\//i,
PS3: /playstation 3/i,
PSP: /playstation portable/i,
Firefox: /firefox|fxios/i,
WinJs: /msapphost/i,
PhantomJS: /phantomjs/i,
AlamoFire: /alamofire/i,
UC: /UCBrowser/i,
Facebook: /FBA[NV]/,
DuckDuckGo: /\sDdg\/[\d.]+$/i
};
this.os = {
Windows11: /\bwindows(?:\s|_|)11(?:\.\d+)?/i,
Windows10: /windows nt 10\.0/i,
Windows81: /windows nt 6\.3/i,
Windows8: /windows nt 6\.2/i,
Windows7: /windows nt 6\.1/i,
UnknownWindows: /windows nt 6\.\d+/i,
WindowsVista: /windows nt 6\.0/i,
Windows2003: /windows nt 5\.2/i,
WindowsXP: /windows nt 5\.1/i,
Windows2000: /windows nt 5\.0/i,
WindowsPhone81: /windows phone 8\.1/i,
WindowsPhone80: /windows phone 8\.0/i,
OSXCheetah: /os x 10[._]0/i,
OSXPuma: /os x 10[._]1(\D|$)/i,
OSXJaguar: /os x 10[._]2/i,
OSXPanther: /os x 10[._]3/i,
OSXTiger: /os x 10[._]4/i,
OSXLeopard: /os x 10[._]5/i,
OSXSnowLeopard: /os x 10[._]6/i,
OSXLion: /os x 10[._]7/i,
OSXMountainLion: /os x 10[._]8/i,
OSXMavericks: /os x 10[._]9/i,
OSXYosemite: /os x 10[._]10/i,
OSXElCapitan: /os x 10[._]11/i,
MacOSSierra: /os x 10[._]12/i,
MacOSHighSierra: /os x 10[._]13/i,
MacOSMojave: /os x 10[._]14/i,
MacOSCatalina: /os x 10[._]15/i,
MacOSBigSur: /(mac os x 10[._]16(?:[._]\d+)?|mac os (?:x )?11[._]\d+)/i,
MacOSMonterey: /mac os (?:x )?12[._]\d+/i,
MacOSVentura: /mac os (?:x )?13[._]\d+/i,
MacOSSonoma: /mac os (?:x )?14[._]\d+/i,
MacOSSequoia: /mac os (?:x )?15[._]\d+/i,
MacOSTahoe: /mac os (?:x )?26[._]\d+/i,
Mac: /os x/i,
Linux: /linux/i,
Linux64: /linux x86_64/i,
ChromeOS: /cros/i,
Wii: /wii/i,
PS3: /playstation 3/i,
PSP: /playstation portable/i,
iOS: /ios/i,
Bada: /Bada\/(\d+)\.(\d+)/i,
Curl: /curl\/(\d+)\.(\d+)\.(\d+)/i,
Electron: /Electron\/(\d+)\.(\d+)\.(\d+)/i
};
this.platform = {
Windows: /windows nt/i,
WindowsPhone: /windows phone/i,
Mac: /macintosh/i,
Linux: /linux/i,
Wii: /wii/i,
Playstation: /playstation/i,
iPad: /ipad/i,
iPod: /ipod/i,
iPhone: /iphone/i,
Android: /android/i,
Blackberry: /blackberry/i,
Samsung: /samsung/i,
Curl: /curl/i,
Electron: /Electron/i,
iOS: /^ios-/i
};
this.Agent = createDefaultAgent();
}
reset() {
this.Agent = createDefaultAgent();
return this;
}
testNginxGeoIP(headers) {
Object.entries(headers != null ? headers : {}).forEach(([key, value]) => {
if (/^GEOIP/i.test(key) && value !== void 0) {
this.Agent.geoIp[key] = Array.isArray(value) ? value.join(",") : value;
}
});
return this;
}
/**
* Parse User-Agent Client Hints from HTTP headers
* @see https://wicg.github.io/ua-client-hints/
*/
parseClientHints(headers) {
var _a, _b, _c, _d, _e, _f, _g;
const resolveHeader = (value) => {
try {
if (value === null || value === void 0) {
return "";
}
if (Array.isArray(value)) {
const first = value[0];
if (typeof first !== "string") {
return "";
}
return first.slice(0, _UserAgent.MAX_HEADER_LENGTH);
}
if (typeof value !== "string") {
return "";
}
return value.slice(0, _UserAgent.MAX_HEADER_LENGTH);
} catch (e) {
return "";
}
};
if (headers === null || headers === void 0 || typeof headers !== "object") {
return null;
}
const normalizedHeaders = {};
try {
let headerCount = 0;
const maxHeaders = 50;
for (const [key, value] of Object.entries(headers)) {
if (++headerCount > maxHeaders) break;
if (typeof key !== "string") continue;
normalizedHeaders[key.toLowerCase()] = resolveHeader(value);
}
} catch (e) {
return null;
}
const secChUa = normalizedHeaders["sec-ch-ua"];
if (!secChUa) {
return null;
}
const parseBrandList = (header) => {
try {
if (!header || typeof header !== "string") return [];
const brands = [];
const brandRegex = /"([^"]{1,128})";\s*v="([^"]{1,64})"/g;
let match;
let iterations = 0;
while ((match = brandRegex.exec(header)) !== null) {
if (++iterations > _UserAgent.MAX_BRAND_COUNT) break;
brands.push({ brand: match[1], version: match[2] });
}
return brands;
} catch (e) {
return [];
}
};
const parseMobile = (header) => {
try {
if (typeof header !== "string") return false;
return header === "?1";
} catch (e) {
return false;
}
};
const parseQuotedString = (header) => {
try {
if (typeof header !== "string") return "";
const truncated = header.slice(0, 256);
const match = /^"([^"]*)"$/.exec(truncated);
return match ? match[1] : truncated;
} catch (e) {
return "";
}
};
try {
const clientHints = {
brands: parseBrandList(secChUa),
mobile: parseMobile((_a = normalizedHeaders["sec-ch-ua-mobile"]) != null ? _a : ""),
platform: parseQuotedString((_b = normalizedHeaders["sec-ch-ua-platform"]) != null ? _b : ""),
platformVersion: parseQuotedString((_c = normalizedHeaders["sec-ch-ua-platform-version"]) != null ? _c : ""),
architecture: parseQuotedString((_d = normalizedHeaders["sec-ch-ua-arch"]) != null ? _d : ""),
bitness: parseQuotedString((_e = normalizedHeaders["sec-ch-ua-bitness"]) != null ? _e : ""),
model: parseQuotedString((_f = normalizedHeaders["sec-ch-ua-model"]) != null ? _f : ""),
fullVersionList: parseBrandList((_g = normalizedHeaders["sec-ch-ua-full-version-list"]) != null ? _g : "")
};
this.Agent.clientHints = clientHints;
return clientHints;
} catch (e) {
return null;
}
}
/**
* Test for DuckDuckGo browser using both Client Hints and UA string patterns
* - Chromium platforms (Android, Windows): Sec-CH-UA brand "DuckDuckGo"
* - WebKit platforms (iOS, macOS): UA string ends with " Ddg/X.Y.Z"
*/
testDuckDuckGo() {
var _a, _b, _c, _d, _e;
if ((_a = this.Agent.clientHints) == null ? void 0 : _a.brands) {
const hasDdgBrand = this.Agent.clientHints.brands.some(
(brand) => brand.brand === "DuckDuckGo"
);
if (hasDdgBrand) {
this.Agent.isDuckDuckGo = true;
this.Agent.browser = "DuckDuckGo";
this.Agent.version = (_b = this.getDuckDuckGoVersion()) != null ? _b : "unknown";
return;
}
}
if ((_c = this.Agent.clientHints) == null ? void 0 : _c.fullVersionList) {
const hasDdgBrand = this.Agent.clientHints.fullVersionList.some(
(brand) => brand.brand === "DuckDuckGo"
);
if (hasDdgBrand) {
this.Agent.isDuckDuckGo = true;
this.Agent.browser = "DuckDuckGo";
this.Agent.version = (_d = this.getDuckDuckGoVersion()) != null ? _d : "unknown";
return;
}
}
if (DUCKDUCKGO_WEBKIT_REGEXP.test(this.Agent.source)) {
this.Agent.isDuckDuckGo = true;
this.Agent.browser = "DuckDuckGo";
this.Agent.version = (_e = this.getDuckDuckGoVersion()) != null ? _e : "unknown";
}
}
testBot() {
const source = this.Agent.source.toLowerCase();
const match = IS_BOT_REGEXP.exec(source);
if (match) {
const botIdentifier = match[1];
if (botIdentifier === "google" && (source.includes("tiktok") || source.includes("trill") || source.includes("bytedance"))) {
this.Agent.isBot = false;
this.Agent.botName = "";
return;
}
this.Agent.isBot = true;
this.Agent.botName = botIdentifier;
} else if (!this.Agent.isAuthoritative) {
this.Agent.isBot = /bot/i.test(this.Agent.source);
this.Agent.botName = this.Agent.isBot ? "bot" : "";
} else {
this.Agent.isBot = false;
this.Agent.botName = "";
}
}
testSmartTV() {
this.Agent.isSmartTV = SMART_TV_REGEXP.test(this.Agent.source.toLowerCase());
}
testMobile() {
if (this.Agent.isWindows || this.Agent.isLinux || this.Agent.isMac || this.Agent.isChromeOS) {
this.Agent.isDesktop = true;
} else if (this.Agent.isAndroid || this.Agent.isSamsung) {
this.Agent.isMobile = true;
}
if (this.Agent.isiPad || this.Agent.isiPod || this.Agent.isiPhone || this.Agent.isBada || this.Agent.isBlackberry || this.Agent.isAndroid || this.Agent.isWindowsPhone) {
this.Agent.isMobile = true;
this.Agent.isDesktop = false;
}
if (MOBILE_REGEXP.test(this.Agent.source)) {
this.Agent.isMobile = true;
this.Agent.isDesktop = false;
}
if (DALVIK_REGEXP.test(this.Agent.source)) {
this.Agent.isAndroidNative = true;
this.Agent.isMobileNative = true;
}
if (OKHTTP_REGEXP.test(this.Agent.source)) {
this.Agent.isAndroidNative = true;
this.Agent.isMobileNative = true;
this.Agent.isMobile = true;
this.Agent.isAndroid = true;
this.Agent.isDesktop = false;
}
if (IOS_SCALE_REGEXP.test(this.Agent.source)) {
this.Agent.isiPhoneNative = true;
this.Agent.isMobileNative = true;
}
}
testAndroidTablet() {
if (this.Agent.isAndroid && !ANDROID_TABLET_REGEXP.test(this.Agent.source)) {
this.Agent.isAndroidTablet = true;
}
}
testTablet() {
if (this.Agent.isiPad || this.Agent.isAndroidTablet || this.Agent.isKindleFire) {
this.Agent.isTablet = true;
}
if (/tablet/i.test(this.Agent.source)) {
this.Agent.isTablet = true;
}
}
testCompatibilityMode() {
if (!this.Agent.isIE) {
return;
}
const tridentMatch = /Trident\/(\d)\.0/i.exec(this.Agent.source);
if (!tridentMatch) {
return;
}
const tridentVersion = parseInt(tridentMatch[1], 10);
const version = parseInt(String(this.Agent.version), 10);
if (version === 7 && tridentVersion === 7) {
this.Agent.isIECompatibilityMode = true;
this.Agent.version = "11.0";
}
if (version === 7 && tridentVersion === 6) {
this.Agent.isIECompatibilityMode = true;
this.Agent.version = "10.0";
}
if (version === 7 && tridentVersion === 5) {
this.Agent.isIECompatibilityMode = true;
this.Agent.version = "9.0";
}
if (version === 7 && tridentVersion === 4) {
this.Agent.isIECompatibilityMode = true;
this.Agent.version = "8.0";
}
}
testSilk() {
if (SILK_REGEXP.test(this.Agent.source)) {
this.Agent.isSilk = true;
}
if (SILK_ACCELERATED_REGEXP.test(this.Agent.source)) {
this.Agent.silkAccelerated = true;
this.Agent.SilkAccelerated = true;
}
return this.Agent.isSilk ? "Silk" : false;
}
testKindleFire() {
const { source } = this.Agent;
if (/KFOT/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire";
}
if (/KFTT/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire HD";
}
if (/KFJWI/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire HD 8.9";
}
if (/KFJWA/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire HD 8.9 4G";
}
if (/KFSOWI/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire HD 7";
}
if (/KFTHWI/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire HDX 7";
}
if (/KFTHWA/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire HDX 7 4G";
}
if (/KFAPWI/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire HDX 8.9";
}
if (/KFAPWA/gi.test(source)) {
this.Agent.isKindleFire = true;
return "Kindle Fire HDX 8.9 4G";
}
return false;
}
testCaptiveNetwork() {
if (/CaptiveNetwork/gi.test(this.Agent.source)) {
this.Agent.isCaptive = true;
this.Agent.isMac = true;
this.Agent.platform = "Apple Mac";
return "CaptiveNetwork";
}
return false;
}
testWebkit() {
if (this.Agent.browser === "unknown" && WEBKIT_REGEXP.test(this.Agent.source)) {
this.Agent.browser = "Apple WebKit";
this.Agent.isWebkit = true;
}
}
testWechat() {
if (WECHAT_REGEXP.test(this.Agent.source)) {
this.Agent.isWechat = true;
this.Agent.version = this.getWechatVersion(this.Agent.source);
}
}
parse(source) {
return new _UserAgent().hydrate(source).Agent;
}
/**
* Hydrate agent from UA string and HTTP headers (including Client Hints)
* This method should be preferred when headers are available as it enables
* detection of browsers that use Client Hints (e.g., DuckDuckGo on Chromium)
*/
hydrateFromHeaders(source, headers) {
this.hydrate(source);
this.parseClientHints(headers);
this.testDuckDuckGo();
return this;
}
hydrate(source) {
this.Agent = createDefaultAgent();
this.Agent.source = source.trim();
this.Agent.os = this.getOS(this.Agent.source);
this.Agent.platform = this.getPlatform(this.Agent.source);
this.Agent.browser = this.getBrowser(this.Agent.source);
this.Agent.version = this.getBrowserVersion(this.Agent.source);
this.Agent.electronVersion = this.getElectronVersion(this.Agent.source);
this.testBot();
this.testSmartTV();
this.testMobile();
this.testAndroidTablet();
this.testTablet();
this.testCompatibilityMode();
this.testSilk();
this.testKindleFire();
this.testCaptiveNetwork();
this.testWebkit();
this.testWechat();
return this;
}
getBrowser(string) {
const agent = this.Agent;
if (this.browsers.YaBrowser.test(string)) {
agent.isYaBrowser = true;
return "YaBrowser";
}
if (this.browsers.AlamoFire.test(string)) {
agent.isAlamoFire = true;
return "AlamoFire";
}
if (this.browsers.Edge.test(string)) {
agent.isEdge = true;
return "Edge";
}
if (this.browsers.PhantomJS.test(string)) {
agent.isPhantomJS = true;
return "PhantomJS";
}
if (this.browsers.Konqueror.test(string)) {
agent.isKonqueror = true;
return "Konqueror";
}
if (this.browsers.Amaya.test(string)) {
agent.isAmaya = true;
return "Amaya";
}
if (this.browsers.Epiphany.test(string)) {
agent.isEpiphany = true;
return "Epiphany";
}
if (this.browsers.SeaMonkey.test(string)) {
agent.isSeaMonkey = true;
return "SeaMonkey";
}
if (this.browsers.Flock.test(string)) {
agent.isFlock = true;
return "Flock";
}
if (this.browsers.OmniWeb.test(string)) {
agent.isOmniWeb = true;
return "OmniWeb";
}
if (this.browsers.Opera.test(string)) {
agent.isOpera = true;
return "Opera";
}
if (this.browsers.Chromium.test(string)) {
agent.isChrome = true;
return "Chromium";
}
if (this.browsers.Facebook.test(string)) {
agent.isFacebook = true;
return "Facebook";
}
if (this.browsers.Chrome.test(string)) {
agent.isChrome = true;
return "Chrome";
}
if (this.browsers.WinJs.test(string)) {
agent.isWinJs = true;
return "WinJs";
}
if (this.browsers.IE.test(string)) {
agent.isIE = true;
return "IE";
}
if (this.browsers.Firefox.test(string)) {
agent.isFirefox = true;
return "Firefox";
}
if (/android/i.test(string) && /version\//i.test(string) && /mobile safari\//i.test(string) && !/chrome/i.test(string) && !/silk/i.test(string)) {
agent.isAndroid = true;
return "Android Browser";
}
if (this.browsers.Safari.test(string)) {
agent.isSafari = true;
return "Safari";
}
if (this.browsers.PS3.test(string)) {
return "ps3";
}
if (this.browsers.PSP.test(string)) {
return "psp";
}
if (this.browsers.UC.test(string)) {
agent.isUC = true;
return "UCBrowser";
}
if (string.includes("Dalvik")) {
return "unknown";
}
if (!string.startsWith("Mozilla")) {
const guess = readProductToken(string);
if (guess) {
agent.isAuthoritative = false;
return guess.name;
}
}
return "unknown";
}
getBrowserVersion(string) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
const agent = this.Agent;
const browser = agent.browser;
switch (browser) {
case "Edge":
return (_a = readVersionAfterKnownProduct(string, ["edge", "edga", "edgios", "edg"])) != null ? _a : "unknown";
case "PhantomJS":
return (_b = readVersionAfterKnownProduct(string, ["phantomjs"])) != null ? _b : "unknown";
case "YaBrowser":
return (_c = readVersionAfterKnownProduct(string, ["yabrowser", "yowser"])) != null ? _c : "unknown";
case "Chrome":
return (_d = readVersionAfterKnownProduct(string, ["chrome", "crios"])) != null ? _d : "unknown";
case "Chromium":
return (_e = readVersionAfterKnownProduct(string, ["chromium"])) != null ? _e : "unknown";
case "Safari":
return (_f = readVersionAfterKnownProduct(string, ["version", "safari"])) != null ? _f : "unknown";
case "Opera":
return (_g = readVersionAfterKnownProduct(string, ["version", "OPR"])) != null ? _g : "unknown";
case "Firefox":
return (_h = readVersionAfterKnownProduct(string, ["firefox", "fxios"])) != null ? _h : "unknown";
case "WinJs":
return (_i = readVersionAfterKnownProduct(string, ["msapphost"])) != null ? _i : "unknown";
case "IE":
return (_j = readInternetExplorerVersion(string)) != null ? _j : "unknown";
case "ps3":
return (_k = readTrailingProductToken(string, true)) != null ? _k : "unknown";
case "psp":
return (_l = readTrailingProductToken(string, false)) != null ? _l : "unknown";
case "Amaya":
return (_m = readVersionAfterKnownProduct(string, ["amaya"])) != null ? _m : "unknown";
case "Epiphany":
return (_n = readVersionAfterKnownProduct(string, ["epiphany"])) != null ? _n : "unknown";
case "SeaMonkey":
return (_o = readVersionAfterKnownProduct(string, ["seamonkey"])) != null ? _o : "unknown";
case "Flock":
return (_p = readVersionAfterKnownProduct(string, ["flock"])) != null ? _p : "unknown";
case "OmniWeb":
return (_q = readVersionAfterKnownPrefix(string, ["omniweb/v"])) != null ? _q : "unknown";
case "UCBrowser":
return (_r = readVersionAfterKnownProduct(string, ["ucbrowser"])) != null ? _r : "unknown";
case "Facebook":
return (_s = readVersionAfterKnownProduct(string, ["FBAV"])) != null ? _s : "unknown";
case "Android Browser":
return (_t = readVersionAfterKnownProduct(string, ["version", "safari"])) != null ? _t : "unknown";
case "DuckDuckGo":
return (_u = this.getDuckDuckGoVersion()) != null ? _u : "unknown";
default:
if (browser !== "unknown") {
return (_v = readVersionAfterProduct(string, browser)) != null ? _v : "unknown";
} else {
this.testWebkit();
if (this.Agent.isWebkit) {
return (_w = readVersionAfterKnownProduct(string, ["applewebkit"])) != null ? _w : "unknown";
}
}
}
return "unknown";
}
getWechatVersion(string) {
const match = string.match(this.versions.Wechat);
return match ? match[1] : "unknown";
}
getDuckDuckGoVersion() {
const hints = this.Agent.clientHints;
if (hints) {
const fullBrand = hints.fullVersionList.find((b) => b.brand === "DuckDuckGo");
if (fullBrand) {
return fullBrand.version;
}
const brand = hints.brands.find((b) => b.brand === "DuckDuckGo");
if (brand) {
return brand.version;
}
}
const match = this.Agent.source.match(this.versions.DuckDuckGo);
return match ? match[1] : null;
}
getElectronVersion(string) {
const match = string.match(this.versions.Electron);
if (match) {
this.Agent.isElectron = true;
return match[1];
}
return "";
}
getOS(string) {
if (this.os.WindowsVista.test(string)) {
this.Agent.isWindows = true;
return "Windows Vista";
}
if (this.os.Windows7.test(string)) {
this.Agent.isWindows = true;
return "Windows 7";
}
if (this.os.Windows8.test(string)) {
this.Agent.isWindows = true;
return "Windows 8";
}
if (this.os.Windows81.test(string)) {
this.Agent.isWindows = true;
return "Windows 8.1";
}
if (this.os.Windows11.test(string)) {
this.Agent.isWindows = true;
return "Windows 11";
}
if (this.os.Windows10.test(string)) {
this.Agent.isWindows = true;
return "Windows 10.0";
}
if (this.os.Windows2003.test(string)) {
this.Agent.isWindows = true;
return "Windows 2003";
}
if (this.os.WindowsXP.test(string)) {
this.Agent.isWindows = true;
return "Windows XP";
}
if (this.os.Windows2000.test(string)) {
this.Agent.isWindows = true;
return "Windows 2000";
}
if (this.os.WindowsPhone81.test(string)) {
this.Agent.isWindowsPhone = true;
return "Windows Phone 8.1";
}
if (this.os.WindowsPhone80.test(string)) {
this.Agent.isWindowsPhone = true;
return "Windows Phone 8.0";
}
if (this.os.Linux64.test(string)) {
this.Agent.isLinux = true;
this.Agent.isLinux64 = true;
return "Linux 64";
}
if (this.os.Linux.test(string)) {
this.Agent.isLinux = true;
return "Linux";
}
if (this.os.ChromeOS.test(string)) {
this.Agent.isChromeOS = true;
return "Chrome OS";
}
if (this.os.Wii.test(string)) {
return "Wii";
}
if (this.os.PS3.test(string)) {
return "Playstation";
}
if (this.os.PSP.test(string)) {
return "Playstation";
}
if (this.os.OSXCheetah.test(string)) {
this.Agent.isMac = true;
return "OS X Cheetah";
}
if (this.os.OSXPuma.test(string)) {
this.Agent.isMac = true;
return "OS X Puma";
}
if (this.os.OSXJaguar.test(string)) {
this.Agent.isMac = true;
return "OS X Jaguar";
}
if (this.os.OSXPanther.test(string)) {
this.Agent.isMac = true;
return "OS X Panther";
}
if (this.os.OSXTiger.test(string)) {
this.Agent.isMac = true;
return "OS X Tiger";
}
if (this.os.OSXLeopard.test(string)) {
this.Agent.isMac = true;
return "OS X Leopard";
}
if (this.os.OSXSnowLeopard.test(string)) {
this.Agent.isMac = true;
return "OS X Snow Leopard";
}
if (this.os.OSXLion.test(string)) {
this.Agent.isMac = true;
return "OS X Lion";
}
if (this.os.OSXMountainLion.test(string)) {
this.Agent.isMac = true;
return "OS X Mountain Lion";
}
if (this.os.OSXMavericks.test(string)) {
this.Agent.isMac = true;
return "OS X Mavericks";
}
if (this.os.OSXYosemite.test(string)) {
this.Agent.isMac = true;
return "OS X Yosemite";
}
if (this.os.OSXElCapitan.test(string)) {
this.Agent.isMac = true;
return "OS X El Capitan";
}
if (this.os.MacOSSierra.test(string)) {
this.Agent.isMac = true;
return "macOS Sierra";
}
if (this.os.MacOSHighSierra.test(string)) {
this.Agent.isMac = true;
return "macOS High Sierra";
}
if (this.os.MacOSMojave.test(string)) {
this.Agent.isMac = true;
return "macOS Mojave";
}
if (this.os.MacOSCatalina.test(string)) {
this.Agent.isMac = true;
return "macOS Catalina";
}
if (this.os.MacOSBigSur.test(string)) {
this.Agent.isMac = true;
return "macOS Big Sur";
}
if (this.os.MacOSMonterey.test(string)) {
this.Agent.isMac = true;
return "macOS Monterey";
}
if (this.os.MacOSVentura.test(string)) {
this.Agent.isMac = true;
return "macOS Ventura";
}
if (this.os.MacOSSonoma.test(string)) {
this.Agent.isMac = true;
return "macOS Sonoma";
}
if (this.os.MacOSSequoia.test(string)) {
this.Agent.isMac = true;
return "macOS Sequoia";
}
if (this.os.MacOSTahoe.test(string)) {
this.Agent.isMac = true;
return "macOS Tahoe";
}
if (this.os.Mac.test(string)) {
this.Agent.isMac = true;
return "OS X";
}
const iPadMatch = readIOSDeviceOSMatch(string, "iPad");
if (iPadMatch) {
this.Agent.isiPad = true;
return iPadMatch;
}
const iPhoneMatch = readIOSDeviceOSMatch(string, "iPhone");
if (iPhoneMatch) {
this.Agent.isiPhone = true;
return iPhoneMatch;
}
if (this.os.Bada.test(string)) {
this.Agent.isBada = true;
return "Bada";
}
if (this.os.Curl.test(string)) {
this.Agent.isCurl = true;
return "Curl";
}
if (this.os.iOS.test(string)) {
this.Agent.isiPhone = true;
return "iOS";
}
if (this.os.Electron.test(string)) {
this.Agent.isElectron = true;
return "Electron";
}
return "unknown";
}
getPlatform(string) {
if (this.platform.Windows.test(string)) {
return "Microsoft Windows";
}
if (this.platform.WindowsPhone.test(string)) {
this.Agent.isWindowsPhone = true;
return "Microsoft Windows Phone";
}
if (this.platform.Mac.test(string)) {
return "Apple Mac";
}
if (this.platform.Curl.test(string)) {
return "Curl";
}
if (this.platform.Electron.test(string)) {
this.Agent.isElectron = true;
return "Electron";
}
if (this.platform.Android.test(string)) {
this.Agent.isAndroid = true;
if (this.platform.Samsung.test(string)) {
this.Agent.isSamsung = true;
}
return "Android";
}
if (this.platform.Blackberry.test(string)) {
this.Agent.isBlackberry = true;
return "Blackberry";
}
if (this.platform.Linux.test(string)) {
return "Linux";
}
if (this.platform.Wii.test(string)) {
return "Wii";
}
if (this.platform.Playstation.test(string)) {
return "Playstation";
}
if (this.platform.iPad.test(string)) {
this.Agent.isiPad = true;
return "iPad";
}
if (this.platform.iPod.test(string)) {
this.Agent.isiPod = true;
return "iPod";
}
if (this.platform.iPhone.test(string)) {
this.Agent.isiPhone = true;
return "iPhone";
}
if (this.platform.Samsung.test(string)) {
this.Agent.isSamsung = true;
return "Samsung";
}
if (this.platform.iOS.test(string)) {
return "Apple iOS";
}
return "unknown";
}
};
/** Maximum header length to process (prevents DoS from oversized headers) */
_UserAgent.MAX_HEADER_LENGTH = 2048;
/** Maximum number of brands to parse from a brand list */
_UserAgent.MAX_BRAND_COUNT = 20;
var UserAgent = _UserAgent;
// src/browser.ts
var singleton = new UserAgent();
var parse = (source) => new UserAgent().parse(source);
var hydrate = (source) => new UserAgent().hydrate(source);
var api = Object.assign(singleton, { UserAgent, parse, hydrate });
var isBrowserWindow = (host2) => typeof host2.window === "object" && host2.window === host2 && typeof host2.document === "object";
var host = globalThis;
if (isBrowserWindow(host)) {
host.UserAgent = UserAgent;
host.useragent = api;
}
var browser_default = api;
return __toCommonJS(browser_exports);
})();
//# sourceMappingURL=express-useragent.global.js.map